container_of宏简介

container_of是Linux内核中一个常用的宏,用于根据结构体成员的地址获取整个结构体的地址。它在内核链表、设备驱动等场景中广泛使用。该宏定义在<linux/kernel.h>中,其原型如下:

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

container_of宏的使用方法

假设有一个结构体struct foo,其中包含一个成员int bar。已知bar的地址,可以通过container_of获取struct foo的地址。

struct foo {
    int baz;
    int bar;
};

int *ptr = &foo_instance.bar;
struct foo *parent = container_of(ptr, struct foo, bar);

container_of的三个参数分别是:

  • ptr:结构体成员的指针。
  • type:结构体的类型。
  • member:成员在结构体中的名称。

container_of宏的实现原理

container_of的核心是计算成员在结构体中的偏移量,然后通过成员的地址减去偏移量得到结构体的起始地址。具体步骤如下:

  1. 使用typeof获取成员的类型,并声明一个临时指针__mptr指向传入的成员地址,这一步主要用于类型检查。
  2. 使用offsetof宏计算成员在结构体中的偏移量。
  3. 将成员地址减去偏移量,得到结构体的起始地址。

container_of宏的使用技巧

技巧1:类型安全检查
container_of宏的第一行通过typeof进行类型检查,确保传入的指针类型与结构体成员的类型匹配。如果类型不匹配,编译器会报错。

技巧2:结合链表使用
container_of常用于内核链表的实现中。例如,通过链表节点的地址获取包含该节点的结构体地址:

struct list_head {
    struct list_head *next, *prev;
};

struct foo {
    int data;
    struct list_head list;
};

struct list_head *node = ...;
struct foo *parent = container_of(node, struct foo, list);

技巧3:避免硬编码偏移量
container_of通过offsetof自动计算偏移量,避免了手动计算偏移量的错误风险。

container_of宏的易错点

易错点1:成员名称拼写错误
如果成员名称拼写错误,编译器不会报错,但会导致运行时计算出错误的结构体地址。例如:

struct foo *parent = container_of(ptr, struct foo, barr); // 错误:成员名应为bar

易错点2:指针类型不匹配
如果传入的指针类型与结构体成员类型不匹配,编译器可能会报出类型不匹配的警告或错误。例如:

char *ptr = (char *)&foo_instance.bar;
struct foo *parent = container_of(ptr, struct foo, bar); // 错误:ptr类型应为int *

易错点3:结构体类型错误
如果结构体类型错误,offsetof会计算出错误的偏移量,导致访问非法内存。例如:

struct bar {
    int x;
    int y;
};

int *ptr = &foo_instance.bar;
struct bar *wrong_parent = container_of(ptr, struct bar, y); // 错误:类型应为struct foo

*易错点4:成员是指针
如果是结构体中的成员是指针类型,则传入的时候要该指针的地址。这个地方需要注意的是,该指针是否是结构体成员中的指针。

struct test {
	int a;
	int b;
	int c;
};
struct test1 {
    struct test *p1;
};
struct test2 {
    struct test *p2;
};

例如,test1和test2中都有一个指向test的指针p1和p2,如果此时要根据p2反向查找test2,但给的参数是p1的指针,那反向得到的会是test1,因此,反向查找时候,只能传递给本成员的指针,别的成员中或许也有该指针,但不能使用!

总结

container_of是Linux内核中一个强大的工具,能够通过成员地址高效地获取结构体地址。使用时需注意成员名称、类型和结构体类型的正确性,避免因拼写错误或类型不匹配导致的运行时错误。结合链表等数据结构使用时,container_of能够显著简化代码并提高可维护性。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐