在Linux内核源码中,经常要对链表进行操作,其中一个很重要的宏是list_for_each_entry:
意思大体如下:假设只有两个结点,则第一个member代表head,list_for_each_entry的作用就是循环遍历每一个pos中的member子项。图1:pos: pos:___________ ____________| | | || | | || ........... | | ................ || | | || | | || member: | _________|__> member || { | | | { || *prev; | | | *prev; || *next;--|---------- | *next;-------------| } | | } | ||—^———— | |____________| | | | | | |_____________________________________________|宏list_for_each_entry: - /**
- 401 * list_for_each_entry - iterate over list of given type
- 402 * @pos: the type * to use as a loop cursor.
- 403 * @head: the head for your list.
- 404 * @member: the name of the list_struct within the struct.
- 405 */
- 406#define list_for_each_entry(pos, head, member) \
- 407 for (pos = list_entry((head)->next, typeof(*pos), member); \
- 408 prefetch(pos->member.next), &pos->member != (head); \
- 409 pos = list_entry(pos->member.next, typeof(*pos), member))
复制代码list_entry((head)->next,
typeof(*pos), member)返回(head)->next物理指针所处位置向前减去offsetof()个字节数据之后,
其父变量pos的物理地址,父变量的类型在编译时由typeof(*pos)自动返回.
所以list_for_each_entry遍历head
下面挂接的类型为typeof(*pos)的childs结构体们,当然每个child结构体包含struct list_head
node之类相似的双向链表list_head类型项,就这样通过循环pos将依次指向双向链表上的各个child.(member就是child类型中
被定义的变量名)
其中用到了函数list_entry():这个函数的作用在图1中表示就是可以通过已知的指向member子项的指针,获得整个结构体的指针(地址)- /**
- 329 * list_entry - get the struct for this entry
- 330 * @ptr: the &struct list_head pointer.
- 331 * @type: the type of the struct this is embedded in.
- 332 * @member: the name of the list_struct within the struct.
- 333 */
- 334#define list_entry(ptr, type, member) \
- 335 container_of(ptr, type, member)
复制代码和函数prefetch: - #define prefetch(x) __builtin_prefetch(x)
复制代码其中用到了builtin_prefetch:prefetch的含义是告诉cpu那些元素有可能马上就要用到,告诉cpu预取一下,这样可以提高速度
其中用到了函数container_of(): - /**
- 487 * container_of - cast a member of a structure out to the containing structure
- 488 * @ptr: the pointer to the member.
- 489 * @type: the type of the container struct this is embedded in.
- 490 * @member: the name of the member within the struct.
- 491 *
- 492 */
- 493#define container_of(ptr, type, member) ({ \
- 494 const typeof( ((type *)0)->member ) *__mptr = (ptr); \
- 495 (type *)( (char *)__mptr - offsetof(type,member) );})
关于offsetof见stddef.h中:
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
TYPE是某struct的类型 0是一个假想TYPE类型struct,MEMBER是该struct中的一个成员. 由于该struct的基地址为0, MEMBER的地址就是该成员相对与struct头地址的偏移量.
关于typeof,这是gcc的C语言扩展保留字,用于声明变量类型.
const typeof( ((type *)0->member ) *__mptr = (ptr);意思是声明一个与member同一个类型的指针常量 *__mptr,并初始化为ptr.
(type *)( (char *)__mptr - offsetof(type,member) );意思是__mptr的地址减去member在该struct中的偏移量得到的地址, 再转换成type型指针. 该指针就是member的入口地址了.
(char*)_mptr - offset)此处先把_mptr指针转化为字符形指针
(为什么这么做呢? 如果_mptr为整形指针 _mptr - offset 相当于减去 sizeof(int)*offset个字节)
减去 offset值 相当于 得到_mptr所在结构体的首地址(即stu的地址)
然后我们把 该地址 强制转化为 struct student类型即可正常使用了*/原理:
struct TYPE
{
int a;
char c[5];
int member;
};
typedef struct TYPE type;
type * p = malloc(sizeof(type));
printf("p=[%d] &p->meber=[%d]\n", p, &p->member);
printf("(char *)&p->member-12=[%d]\n",(char *)&p->member - 12);
printf("(int *)&p->member -12=[%d]\n",(int *)&p->member - 12);
执行结果:
p=[278523920] &p->meber=[278523932]
(char *)&p->member-12=[278523920] =
278523932 - 12*sizeof(char)(int *)&p->member -12=[278523884] =
278523932 - 12*sizeof(int)其中又用到了offsetof()函数:lxr上找到的源码:
- #define offset_of(type, memb) \
- 47 ((unsigned long)(&((type *)0)->memb))
offsetof(TYPE, MEMBER)
该宏在Linux内核代码(版本2.6.22)中定义如下:
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER);
分析:
(TYPE
*)0,将 0 强制转换为 TYPE 型指针,记 p = (TYPE *)0,p是指向TYPE的指针,它的值是0。那么
p->MEMBER 就是 MEMBER
这个元素了,而&(p->MEMBER)就是MENBER的地址,而基地址为0,这样就巧妙的转化为了TYPE中的偏移量。再把结果强制转
换为size_t型的就OK了,size_t其实也就是int。
typedef __kernel_size_t size_t;
typedef unsigned int __kernel_size_t;
可见,该宏的作用就是求出MEMBER在TYPE中的偏移量。
阅读(549) | 评论(0) | 转发(0) |