内核双链表 list_entry实现

list_entry(ptr, type, member)语句理解

  list_entry(ptr, type, member)语句定义在文件include/linux/list.h中。

00342: /**

00343:  *list_entry-getthestructforthisentry

00344:  *@ptr:the&structlist_headpointer.

00345:  *@type:thetypeofthestructthisisembeddedin.

00346:  *@member:thenameofthelist_structwithinthestruct.

00347:  */

00348: #definelist_entry(ptr type member)\

00349:       container_of(ptr,type,member)

00350:

 

   而container_of()宏定义在文件include/linux/kernel.h。

00670: /**

00671:  *container_of-castamemberofastructureouttothecontainingstructure

00672:  *@ptr:thepointertothemember.

00673:  *@type:thetypeofthecontainerstructthisisembeddedin.

00674:  *@member:thenameofthememberwithinthestruct.

00675:  *

00676:  */

00677: #definecontainer_of(ptr type member)({             \

00678:       consttypeof(((type*)0)–>member)*__mptr=(ptr);  \

00679:       (type*)((char*)__mptroffsetof(type,member));})

   

list_entry(ptr, type, member)宏的含义为:已知指针ptr所指向的地址,通过ptr所指向的地址来计算其所属数据结构的起始地址(ptr的指针类型和member是同一种数据结构,在这里就是struct list_head结构类型,现在是内核中的双链表,这种思想也可以用在其他数据结构种)。

 

1、 (type *)0

将地址0转换为type类型的指针。

如我们可以将数据结构struct page应用到这里。

(struct page *)0

很多人对这条语句感到困惑,一般我们程序中不能访问地址0的,那怎么能够将地址0转换为type类型的指针呢?这条语句是否符合语法,是否正确?

不同进程看到的地址空间是不一样的,具体类型取决于进程访问的方式。前面我们提到类型强制性转换,就是可以将任何地址强制性转换为某种数据类型。对于进程来说,不一定能够访问0地址(访问0地址是违法的),但只要不去访问0地址,我们把地址转换为任何类型都是没问题的。怎样想与怎样去做是两回事,可以把地址0想象为任何数据类型,但只要不去做(访问地址0),那就没有任何问题。


2、 offsetof(TYPE, MEMBER)

  offsetof()宏定义在文件include/linux/stddef.h。

00020: #undefoffsetof

00021: #ifdef__compiler_offsetof

00022: #defineoffsetof(TYPE,MEMBER)__compiler_offsetof(TYPE,MEMBER)

00023: #else

00024: #defineoffsetof(TYPE MEMBER)((size_t)&((TYPE*)0)–>MEMBER)

00025: #endif

00026: #endif/*   KERNEL   */

00027:

这句话的目的是为了计算成员member在type数据结构中的偏移量。

根据前一节的内容,我们把地址0强制性转换为type类型的指针,type类型指针中有何成员变量member,那么成员变量member的地址是多少?

假设地址0已经是type类型的指针,那么成员变量member的地址为&(0->member)(不难理解吧)。

前面我们只是假设地址0为type类型的指针,那需要先强制性转换为type类型的指针。那么得到地址0中成员变量member的方式就变为

&((TYPE*)0)–>MEMBER

因为每个结构体在编译时,已知道其大小和各个成员变量大小,就可以简洁方便地计算偏移量。这个偏移量在程序编译时便可确定。

这个地址类型是member的,为了以后的计算,我们需将这个地址转换为无符号长整型(size_t)。

 (size_t)&((TYPE*)0)–>MEMBER

                                                           

3、typeof

   从字面意思上理解,typeof就是获取其类型,其含义也正是如此。关键字typeof返回的是表达式的类型,使用上类似于关键字sizeof,但它的返回值是类型,而不是一个大小。下面是一些例子:

char *chptr; // A char pointer

typeof (*chptr) ch; // A char

typeof (ch) *chptr2; // A char pointer

typeof (chptr) chparray[10]; // Ten char pointers

typeof (*chptr) charray[10]; // Ten chars

typeof (ch) charray2[10]; // Ten chars


4、 const typeof( ((type *)0)- >member ) *__mptr

   定义一个常量指针__mptr,指针类型是和结构体type中成员变量member数据类型一致。


语句consttypeof(((type*)0)–>member)*__mptr=(ptr);的含义为定义一个临时常量指针__mptr,并把ptr赋值给__mptr。


5、(type *)( (char *)__mptr – offsetof(type,member) )

我们已经得到type类型数据结构(这个结构有个成员变量是member)变量的起始地址,结果是无符号整型的,为了真正能使用这个数据结构,还需最后一步,将其强制性转换为type指针类型。

(type*)((char*)__mptroffsetof(type,member))

你可能感兴趣的:(linux)