MudOS简单源码剖析

暂时没任务,了解一下MudOS的源码。推荐使用vim来看,搭配上ctags插件跳转函数定义。

下面先来讲一讲数据类型:array、mapping、string。

array

array类似于其他语言中线性表的概念,比如说:c++语言的vector、python的list。array.h中定义了结构体array_t,array_t就是主要的存储数据的类型:

typedef struct array_s { 
    unsigned short ref;  
    int extra_ref;
    unsigned short size;
    svalue_t item[1];
}

上面的svalue_t item[1];就是存储具体数据的地方。明显是一个数组。svaluet_t是一个结构体类型,定义在lpc.h中:

 37 typedef struct svalue_s {
 38     short type;
 39     short subtype;
 40     union u u;
 41 } svalue_t;    

 11 union u {
 12     char *string;
 13     int number;
 14     float real;
 15 
 16     refed_t *refed; /* any of the block below */
 21     struct object_s *ob;
 22     struct array_s *arr;
 23     struct mapping_s *map;
 24     struct funptr_s *fp;
 25 
 26     struct svalue_s *lvalue;
 27     struct ref_s *ref;
 28     unsigned char *lvalue_byte;
 29     void (*error_handler) PROT((void));
 30 };

通过上面基本上了解到array是如何定义的了。

比较有意思的是array提供的一个排序函数:f_sort_array。

内部使用的是快速排序,快速排序定义在qsort.c中。

可以看到关于快排的注释:

/* qsort adapted from page 87 of K&R 2nd edition */ 

Mudos作者引用了K&R写的c程序设计第2版的87页。

附上array支持的函数:

allocate() - 配置内存给一个有 size 个元素的数组
arrayp() - 看一个给定的变量是否为数组 (array).
filter_array() - 返回一个过滤旧数组内容的新数组.
map_array() - 经由一个函数修改一个数组的元素 (element)
member_array() -  在一个数组 (array)  或字符串 (string) 中找寻指定的项目 (item)
pointerp() -  判断指定的变量是否为一个数组 (array).
sort_array() - 将一个数组的内容排序.
unique_array() - 将一个物件数组分组.

mapping

在mapping.h中定义了mapping在内存中实际的类型:

 32 typedef struct mapping_s {                                                                                                                                                                       
 33     unsigned short ref;     /* how many times this map has been* referenced */                                                                                                                                                                                                                                                                                                                                                      
 38     mapping_node_t **table; /* the hash table */                                                                                                                                                 
 39     unsigned short table_size;  /* # of buckets in hash table == power of 2 */                                                                                                                   
 40     unsigned short unfilled;    /* # of buckets among 80% of total buckets that do not have entries */                                                                                           
 41     unsigned int count;     /* total # of nodes actually in mapping  */                                                                                                                                                                                                                                                                                                                   
 45 } mapping_t; 

 11 typedef struct mapping_node_s {
 12     struct mapping_node_s *next;    
 13     svalue_t values[2];    
 14 } mapping_node_t; 

从注释很容易可以看出来,mapping底层是用hash_table来存储数据的。

很想吐槽这注释....

mapping_node_t **table;可以知道,使用的是链地址法的方式实现的hashtable。

下面看一个返回所有value值的函数实现(mapping.c):

1256 /* mapping_values */                                                                                                                                                  
1257                                                                                                                                                                       
1258 array_t *                                                                                                                                                             
1259 mapping_values P1(mapping_t *,m)                                                                                                                                      
1260 {                                                                                                                                                                     
1261     array_t *v;                                                                                                                                                       
1262     int j = m->table_size;                                                                                                                                            
1263     mapping_node_t *elt, **a = m->table;                                                                                                                              
1264     svalue_t *sv;                                                                                                                                                     
1265                                                                                                                                                                       
1266     debug(mapping,("mapping_values: size = %d\n",MAP_COUNT(m)));                                                                                                      
1267                                                                                                                                                                       
1268     v = allocate_empty_array(MAP_COUNT(m));                                                                                                                           
1269     sv = v->item;                                                                                                                                                     
1270     do {
1271         for (elt = a[j]; elt; elt = elt->next)
1272         assign_svalue_no_free(sv++, elt->values + 1);
1273     } while (j--);
1274     return v;
1275 }

很容易就能看懂。其实就是对于mapping的一次遍历。

附上mappings支持的函数:

allocate_mapping() - 预先配置 (pre-allocate) 一块内存给一个映射 (mapping).
each() - 分次返回一个映射变量 (mapping) 的各个 (key, value) 的内容.
filter_mapping() - 以一个函数为准, 移除一个映射变量中的某些元素.
keys() - 返回在一个映射变量 (mapping) 中所有 (关键字, 内容值) (即 (key, value) ) 数据关键字的数组 (array).
values() - 从一个映射变量的 (关键字, 内容值) ( (keys, values) )  中, 以数组返回内容值.
map_delete() -  以关键字从一个映射变量中删除一对 (关键字, 内容值) ((key, value)).
map_mapping() - 经由一个函数修改一个映射变量中的元素
mapp() - 判断一个指定的变量是否为映射变量 (mapping).
match_path() - 在一个映射变量 (mapping) 中找寻路径名称 (path)
unique_mapping() - 以一个函数为准, 从一个数组另创一个映射 (mapping).

string

string其实就是一个对于字符数组的封装。

string在mudos内的定义位于stralloc.h和stralloc.c中。来看看stralloc.h声明的函数有:

125 void init_strings PROT((void));  //初始化字符串                                                                                                                        
126 char *findstring PROT((char *));  //查找字符串                                                                                                                       
127 char *make_shared_string PROT((char *));  //共享字符串                                                                                                                 
128 char *ref_string PROT((char *));  //引用字符串                                                                                                                         
129 void free_string PROT((char *));  //释放字符串                                                                                                                         
130 void deallocate_string PROT((char *));  //解除string的内存分配                                                                                                         
131 int add_string_status PROT((outbuffer_t *, int));  //增加字符串的状态                                                                                                      

来看一下init_strings函数:

 85 void init_strings()                                                                                                                                                    
 86 {                                                                                                                                                                       87     int x, y;                                                                                                                                                          
 88                                                                                                                                                                        
 89     /* ensure that htable size is a power of 2 */                                                                                                                      
 90     y = HTABLE_SIZE;                                                                                                                                                   
 91     for (htable_size = 1; htable_size < y; htable_size *= 2)                                                                                                           
 92     ;                                                                                                                                                                  
 93     htable_size_minus_one = htable_size - 1;                                                                                                                           
 94     base_table = CALLOCATE(htable_size, block_t *,                                                                                                                     
 95                TAG_STR_TBL, "init_strings");                                                                                                                                                                                                                                                                                      
 99                                                                                                                                                                        
100     for (x = 0; x < htable_size; x++) {                                                                                                                                
101     base_table[x] = 0;                                                                                                                                                 
102     }                                                                                                                                                                  
103 }  

mudos的底层是通过hashtable来管理string的。init_strings函数就是初始化这个hashtable。

再来看看findstring :

141 char *
142      findstring P1(char *, s)
143 {
144     block_t *b;
145 
146     if ((b = findblock(s))) {
147     return STRING(b);
148     } else {
149     return (NULL);
150     }
151 }

 67 #define findblock(s) sfindblock(s, StrHash(s))

112 INLINE_STATIC block_t *
113         sfindblok P2(char *, s, int, h)
114 {
115     block_t *curr, *prev;
116 
117     curr = base_table[h];
118     prev = NULL;
119 #ifdef STRING_STATS
120     num_str_searches++;
121 #endif
122 
123     while (curr) {
124 #ifdef STRING_STATS
125     search_len++;
126 #endif
127     if (*(STRING(curr)) == *s && !strcmp(STRING(curr), s)) {    /* found it */
128         if (prev) {     /* not at head of list */
129         NEXT(prev) = NEXT(curr);
130         NEXT(curr) = base_table[h];
131         base_table[h] = curr;
132         }
133         return (curr);  /* pointer to string */
134     }
135     prev = curr;
136     curr = NEXT(curr);
137     }
138     return ((block_t *) 0); /* not found */
139 }

find_string就是在hashtable中去查找给定字符串。查找步骤如下:

1、通过hash(s)确定s所在的桶。
2、顺序查找单链表。
3、如果找到了,并且不是单链表头结点,就提前到头结点。
4、返回结果。

mudos的string也是用了慢拷贝来做的。慢拷贝是指如果新定义的字符串已经存在的话,就增加对这个字符串的引用,而不是新开一块内存去存储。这样的做法就导致了,string不能改,改起来会特别麻烦。

每个string都带了一个引用计数器。一般这种引用计数的话,在多线程情况下会出现竞争,因为i++不是原子的,转换成汇编语句会变成:

mov eax,【xxxxxxxx】
inc   eax
```

因为两条语句中间可能会出现中断,move指令执行之后,此时如果转入到另外一条线程下面,刚好也执行了move指令,那么两个线程各自的eax都加了一,但是实际上应该加二才对。

一种比较简单处理是在原子语句之间先关闭中断,然后进行原子操作后再
打开中断就OK了。

efuns提供的关于string的函数有:

```
stringp() - 判断一个变量是否为字符串类型 (string).
break_string() - 以固定的间隔打断一个字符串 (string).返回一个字符串
capitalize() - 把一个字符串的第一个英文字符 (character) 转成大写.
clear_bit() - 清除一个字符串中的某一个 bit .
crypt() - 对一个字符串进行编码.
explode() - 打断一个字符串.返回字符串数组
implode() - 连结多个字符串.
lower_case() - 转换一个指定字符串的内容为小写
reg_assoc() - 一个正规样式 (regular pattern) 子字符串撷取器(extractor)
regexp() - 正规描述式(regular expression)处理程序
replace_string() - 替换一个字符串中符合条件的字.
set_bit() - 设定一个位元字符串 (bitstring) 中的一个位元 (bit).
printf, sprintf - 转换成指定格式的输出结果.
sscanf() - 从一个字符串中读进与指定格式相符的数据.
strcmp() - 判断两个字符串间的语句关系 (lexical relationship).
strlen() - 返回一个字符串的长度
strsrch() - 在一个字符串中寻找特定的字符串.
test_bit() - 检查一个位元字符串 (bitstring) 中某一个位元 (bit) 的值.
```





最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,752评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,100评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,244评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,099评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,210评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,307评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,346评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,133评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,546评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,849评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,019评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,702评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,331评论 3 319
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,030评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,260评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,871评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,898评论 2 351

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,644评论 18 139
  • 一、基本数据类型 注释 单行注释:// 区域注释:/* */ 文档注释:/** */ 数值 对于byte类型而言...
    龙猫小爷阅读 4,257评论 0 16
  • 国家电网公司企业标准(Q/GDW)- 面向对象的用电信息数据交换协议 - 报批稿:20170802 前言: 排版 ...
    庭说阅读 10,936评论 6 13
  • 不要告别 不要离开 如果 假使 那又怎样 你听不懂 也做不到。
    秋柄阅读 171评论 0 0
  • *从幼儿园到研究所,在不同国家求学,被三个不同的文化打磨,走了这么多年,我越来越明白,理家,才是自己生命中最宝贵的...
    Sophia丹萍阅读 238评论 0 0