今天在看linux的内核链表这一章时,看到了很有意思的一个东西,几下来分享下,同时写一点我的看法。
谈到内核链表,在linux源码里我们会看到铺天盖地的链表,内核里有那么多的结构体,那么内核源码是怎么样将这些链表高效的操作的呢。
我觉得内核源码力里的第一个亮点就是这个双向链表struct list_head的设计。我们知道内核里面有太多的结构体,如果每个结构体都做一个链表的话那将是灾难性的,不仅不方便管理,代码复用性不高,而且阅读起来也很没有美感。所以内核里设计了这样一个双链表结构体
`struct list_head {
struct list_head *next, *prev;};`
其他结构体当需要构成链表时,只需要包含这个样一个struct list_head成员,而不需要定义指向其本身结构体的指针。这种内核链表的设计既方便管理又提高了代码的复用性。
另一个不得不说的就是这个list_entry了,大家先来看一下摘自百度百科上的list_entry实现
#define list_entry(ptr,type,member) ((type*)((char*)ptr - (unsigned long)(&((type*)0)->member)))
我们从右到左每个单词的来分析,先将0地址强制转换成type类型,再通过&(0->member)取得member成员的地址,其实这就是member成员在type结构体内的偏移地址了,因为我的type的地址是0嘛!
现在把指向member成员的指针ptr(类型是list_head)强制转换成字节型,然后再减去上述偏移地址,最后再做一步强制转换,变成type类型的指针。
巴拉巴拉说了那么多,我们用这个宏干嘛了?最终返回给我们的就是指向你需要的特定结构体type的指针啊!你就可以通过这个指针访问特定结构体内的成员了!
再来欣赏下Linux内核中的源码(2.6.3版本)
/**
* list_entry - get the struct for this entry
* @ptr: the &struct list_head pointer.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_struct within the struct.
*/
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
/**
* 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) );})
总结一下,只要我们的结构体type中含有list_head类型的member成员,就可以通过指向member的指针ptr访问这个结构体type。
最后说一点题外话,不知道做过嵌入式题目的同学们看到这一行代码有没有似曾相识的感觉?哪家公司的笔试题我就不点名啦。