list_entry()详解

Linux内核中,获取节点地址的函数list_entry()非常常用,由于其定义有点晦涩,先解析如下:

list_entry的宏定义:
#define list_entry(ptr, type, member) / 
((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) 
这个倒是不难理解:从一个结构的成员指针找到其容器的指针。
但是正因为如此,我的第一感觉是,这个宏的名字应该更加抽象,名字似乎应该改称叫“寻找容器”一类的,查看list.h源代码,发现现在的定义是这样的:
#define list_entry(ptr, type, member) /
    container_of(ptr, type, member)

#define container_of(ptr, type, member)                 /
({                                                        /
    const typeof( ((type *)0)->member ) *__mptr = (ptr);/
    (type *)( (char *)__mptr -  offsetof(type,member) ); /
})

#define  offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
linux不想用C++,但又想利用C++的优点,如是出现了很多奇怪的宏,他们叫做trick。
ptr是找容器的那个变量的指针,把它减去自己在容器中的偏移量的值就应该 得到容器的指针。(容器就是包含自己的那个结构)。指针的加减要注意类型,用(char*)ptr是为了计算字节偏移。((type *)0)->member是一个小技巧。自己理解吧。前面的(type *)再转回容器的类型。
===================================================================================
#define list_entry(ptr, type, member) /
        ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))

    ptr是指向list_head类型链表的指针,type为一个结构,而member为结构type中的一个域,类型为list_head,这个宏返回指向type结构的指针。在内核代码中大量引用了这个宏,因此,搞清楚这个宏的含义和用法非常重要。

   
设有如下结构体定义:
typedef struct xxx
{
     ……(结构体中其他域,令其总大小为size1)
     type1 member;
     ……(结构体中其他域)
}type;


定义变量:
   type a;
   type * b;
   type1 * ptr;
执行:
   ptr=&(a.member);
   b=list_entry(ptr,type,member);
则可使b指向a,得到了a的地址。

如何做到的呢?

先看&((type *)0)->member:
把“0”强制转化为指针类型,则该指针一定指向“0”(数据段基址)。因为指针是“type *”型的,所以可取到以“0”为基地址的一个type型变量member域的地址。那么这个地址也就等于member域到结构体基地址的偏移字节数。

 


再来看 ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))):
(char *)(ptr)使得指针的加减操作步长为一字节,(unsigned long)(&((type *)0)->member)等于ptr指向的member到该member所在结构体基地址的偏移字节数。二者一减便得出该结构体的地址。转换为 (type *)型的指针,大功告成。

==============

 list_entry定义如下:

/**
* 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) /
        ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
===================================================================================
initcall_from_entry函数是Linux内核初始化过程中的一部分,其作用是将定义为__initcall的函数指针添加到初始化调用链表中。这个函数被定义在init/main.c中。 具体来说,当内核启动时,会执行一系列的初始化函数,例如设备驱动初始化、内存管理初始化等。这些初始化函数被定义为__initcall,表示它们只在内核启动时被调用一次,并且在初始化完成后会被释放掉。这些函数指针被存储在一个初始化调用链表中。initcall_from_entry函数就是将这些函数指针添加到初始化调用链表中的。 initcall_from_entry函数的参数是一个函数指针,指向一个定义为__initcall的初始化函数。函数的实现比较简单,它会将传入的函数指针添加到初始化调用链表中。具体的实现过程可以参考以下代码: ```c static void __init initcall_from_entry(initcall_entry_t *entry) { initcall_t fn = (initcall_t) entry->function; int level = initcall_level(entry); if (level != -1) { list_add_tail(&entry->list, &initcall_levels[level]); printk(KERN_DEBUG "Registered initcall %s with priority %d\n", entry->name, level); } } ``` 在函数中,首先将传入的函数指针转换成initcall_t类型,并通过initcall_level函数获取该函数的优先级level。如果level不等于-1,表示该函数是一个有效的初始化函数,将其添加到对应优先级的初始化调用链表中。最后打印一条日志信息,表示该函数已经被注册。 总的来说,initcall_from_entry函数是Linux内核初始化过程中的一部分,负责将定义为__initcall的初始化函数添加到初始化调用链表中。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值