内核链表常用宏——container_of()

定义

#define list_entry(ptr, type, member) \
	container_of(ptr, type, member)

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

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

container_of(ptr, type, member)

  • 作用:通过小结构体地址得到其所在的大结构体地址

  • 参数:

    • ptr:从外面输入进来的小结构体指针struct list_head*)(即小结构体的首地址)
    • type:大结构体类型(struct kernel
    • member:大结构体中的小结构体变量的名字(list
  • 使用:

    struct kernel
    {
        datatype data;
        struct list_head list; 
    };
    
    struct list_head *p = (struct list_head *)0x22; 		// 假设小结构体地址为0x22
    struct kernel *xp = list_entry(p, struct kernel, list); // 得到大结构体地址
    
  • 图示

  • 在linux中,依次执行每个语句,然后将最后一条语句的值作为整个的值

    #define xx ({;;;}) 
    
  • 详细分析:

    • const typeof( ((type *)0)->member ) *__mptr = (ptr);

      • typestruct kernelmember为小结构体变量的名字list

      • typeof( ((type *)0)->member )得到小结构体的类型

        • ((TYPE *)0) 将0转换为struct kernel类型的结构体指针,换句话说就是让编译器认为这个结构体是开始于程序段起始位置0
        • (((type *)0)->member) 引用结构体中list成员
      • 整条语句等价于const struct list_head *__mptr = (ptr);

      • 总结:用typeof()获取结构体里list成员属性的类型,然后定义一个该类型的临时指针变量__mptr,并将ptr所指向的list的地址赋给__mptr

        为什么不直接使用 ptr 而要多此一举呢?我想可能是为了避免对 ptr 及prt 指向的内容造成破坏。

    • (type *)( (char *)__mptr - offsetof(type,member) );

      • offsetof(type,member),即((size_t) &((TYPE *)0)->MEMBER)

        • (TYPE *)0 首先将地址 0 强制转换为 struct kernel 类型的指针。这是一个虚拟的地址操作,目的是在逻辑上构建一个指向 struct kernel 类型的指针,但实际上并不真的访问地址 0

        • &((TYPE *)0)->MEMBER 接着取这个虚拟指针所指向的结构体中 list 成员的地址。由于是从地址 0 开始计算,所以得到的地址值实际上就是 list 成员在 struct kernel 结构体中的偏移量

        • 得到小结构体起始位置 到大结构体的起始位置之间的字节数

      • 通过小结构体指针 __mptr 减去(小结构体起始位置到大结构体的起始位置之间的字节数),得到了大结构体的地址

posted @ 2024-08-13 21:02  YOLO_01  阅读(82)  评论(0)    收藏  举报