BBBc
最近一直在读LINUX内核设计与实现,之前自己用C写过单链表的实现,读到第六章内核数据结构时,大感亲切,不似前章晦涩。内核链表结构的所有操作只接受list_head类型的数据作为参数,并依靠list_entry来完成链表的各种管理,读到这里,我想起第三章进程管理中current宏获取当前进程tpid(因为线程组号tpid和进程pid相同)中,在遍历进程时用到的list_entry获取下一个进程:
list_entry(task->tasks.next,struct task_struct,task)
我发现在第六章通过宏定义将list_entry定义成container_of,追踪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) );})
这里的(type *)0使用的是虚拟结构,也就是将结构体首地址设为0,这样 ((type *) 0)->member指向的地址就是相对于首地址的偏移量!同时mptr初始化为ptr,这样mptr减去member的偏移地址就能得到ptr结构体的首地址。我在网上找到另一种写法:
#define list_entry(ptr, type, member) /
((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
所以本意上,container_of和list_entry的作用是一样,都得到当前指向的结构体的地址,这样就能取到这个结构体内的任何成员变量。