穷究链表(十)
今天来看看C实现的链表的经典,也就是linux内核中使用的链表形式。内核代码是在www.kernel.org/处下载的。这里选择了最新的源码包进行下载,为linux-2.6.31.1。其实链表作为最常用的数据结构,应该是变化不大的,虽然没有比较过,但是如果你下载2.4的内核代码,链表部分应该还是一样的。
为了更好的分析代码,这里使用了source insight来辅助分析。主要分析两个文件:list.h和8250.c。
其实linux作为一个大的系统,读它的确是比较困难的。但是如果说其中有什么特别高深的东西,也并不一定,都是一行一行写出来的,而其中的原理,在一般的OS书中也都涉及到了。更底层的东西,在CPU厂商的体系结构手册中,我们也基本不用关心。将其中抽出一部分来看看,还应该是不太难的。关键还是使用,以及考虑全面。
list.h (\linux-2.6.31.1\linux-2.6.31.1\include\linux\list.h)
8250.c (driver/serial/8250.c)
因为整个文件比较长,list.h中有list_head与hlist_head,一共有710行;而8250.c为串口驱动程序,一共有3249行。肯定是不可能像前面一样列出来。而且本人的水平也不可能将其讲解清楚,而且时间也不允许我现在慢慢折腾,做到了然于心
这里尽量将其情况大概描述一遍,等以后如果有项目做到相关,可以再理解添加。
list.h中使用了list_head来做一个“简单的双向链表的实现”,还有一个hlist_head,为“Double linked lists with a single pointer list head.”主要用于哈希表,前面一个有些了解,后面那个也是刚刚接触,所以这里后面这个就简单提一下,以免自己理解不深刻,误导大家。
而8250.c中就是使用这两个结构,主要使用函数为:
INIT_LIST_HEAD
list_add
list_del
list_empty
list_entry
hlist_del
hlist_add_head
hlist_entry
hlist_for_each
一目了然,带h头的为hlist专用,其他为list使用。
在list.h中可以注意到,所有的函数都是使用的static inline的,而其他则是使用宏。在内核中,为了效率,使用这些来减少函数调用的开销是必要的。
其实代码也比较简单,这里也就不讲了。主要有一个hack一点的地方
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
这里调用的container_of为
/**
* container_of - cast a member of a structure out to the containing structure
* @ptr: the pointer to the member.
* @type: the type of the container struct this is embedded in.
* @member: the name of the member within the struct.
*
*/
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
该宏可以在kernel.h中找到,算是比较hack一些的地方。
offsetof宏可以在stddef.h中找到:
#undef offsetof
#ifdef __compiler_offsetof
#define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER)
#else
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#endif
其他可以自己看看,也就是链表添加删除操作。
到这里为止,关于C的部分就结束了,下面就开始写C++如何制作链表程序,写得不好,大家见谅。最近事多心烦,不想在链表上花费太多时间了。赶紧结束,然后进入递归的部分。