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) 的值.
```





你可能感兴趣的:(MudOS简单源码剖析)