Linux内核第一宏:container_of

本文详细介绍了Linux内核中的container_of宏,这是一个用于根据结构体成员地址获取结构体首地址的重要宏。文章从container_of的用途、用法、实现分析等方面展开,通过实例展示了如何使用和理解这个宏,揭示了其在内核驱动开发中的关键作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

static void device_release(struct device *dev)
{
	struct device *rd = to_device(dev);

	devm_kfree(dev, rd);
}

这段代码突然出现to_device函数,

搜索查看定义:grep -r -n "to_device"

#define to_device(obj) container_of(obj, struct device, dev)

container_of什么鬼?网上一搜吓一跳:内核第一宏。

好吧自己小白学习了记录下。


Linux 内核中的 container_of 宏

container_of 宏介绍

有了上面语句表达式和 typeof 的基础知识,接下来我们就可以分析 Linux 内核第一宏:container_of。这个宏在 Linux 内核中应用甚广。会不会用这个宏,看不看得懂这个宏,也逐渐成为考察一个内核驱动开发者 C 语言功底的不成文标准。废话少说,我们还是先一睹芳容吧。

#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) );})

作为内核第一宏,绝对不是盖的:看看这身段,这曲线,高端大气上档次,低调奢华有内涵,不出去再做个头发,简直就是暴殄天物。GNU C 高端扩展特性的综合运用,宏中有宏,不得不佩服内核开发者这天才般地设计。那这个宏到底是干什么的呢?它的主要作用就是:根据结构体某一成员的地址,获取这个结构体的首地址。根据宏定义,我们可以看到,这个宏有三个参数,它们分别是:

  • type:结构体类型
  • member:结构体内的成员
  • ptr:结构体内成员member的地址

也就是说,我们知道了一个结构体的类型,结构体内某一成员的地址,就可以直接获得到这个结构体的首地址。container_of 宏返回的就是这个结构体的首地址。

container_of 宏使用示例

比如现在,我们定义一个结构体类型 student:

struct student
{
    int age;
    int num;
    int math;
};
int main(void)
{
    struct student stu;
    struct student *p;
    p = container_of( &stu.num, struct student, num);
    return 0;
}

在这个程序中,我们定义一个结构体类型 student,然后定义一个结构体变量 stu,我们现在已经知道了结构体成员变量 stu.num 的地址,那我们就可以通过 container_of 宏来获取结构体变量 stu 的首地址。

这个宏在内核中非常重要。我们知道,Linux 内核驱动中,为了抽象,对数据结构体进行了多次封装,往往一个结构体里面嵌套多层结构体。也就是说,内核驱动中不同层次的子系统或模块,使用的是不同封装程度的结构体,这也是 C 语言的面向对象思想。分层、抽象、封装,可以让我们的程序兼容性更好,适配更多的设备,但同时也增加了代码的复杂度。

我们在内核中,经常会遇到这种情况:我们传给某个函数的参数是某个结构体的成员变量,然后在这个

<think>好的,我现在要解决用户遇到的编译错误:`./include/linux/compiler.h:577:38: error: call to ‘__compiletime_assert_390’ declared with attribute error: pointer type mismatch in container_of()`。首先,我需要理解这个错误的原因。 用户提到错误发生在`compiler.h`的第577行,涉及`container_of()`的使用。`container_of`在Linux内核中是一个常用的,用于通过结构体成员的指针来获取整个结构体的指针。这个错误通常是因为类型不匹配导致的,可能是在使用`container_of`时,传入的成员指针类型与结构体中声明的成员类型不一致。 接下来,我需要回顾`container_of`的用法。它的原型通常是这样的: ```c #define container_of(ptr, type, member) ({ \ const typeof(((type *)0)->member) *__mptr = (ptr); \ (type *)((char *)__mptr - offsetof(type, member)); }) ``` 这里,`ptr`是指向结构体成员的指针,`type`是结构体类型,`member`是成员在结构体中的名称。关键点在于,`ptr`的类型必须与结构体中`member`的类型严格匹配,否则在编译时会出现类型检查错误。 用户遇到的错误提示中提到了`__compiletime_assert_390`,这通常表示编译器在编译时进行了静态断言检查,发现类型不匹配。因此,可能的情况是用户在调用`container_of`时,传递的指针类型与结构体成员的类型不符。 接下来,我需要考虑可能的解决方案。首先,检查使用`container_of`的地方,确认三个参数是否正确。特别是第一个参数`ptr`的类型是否与结构体中`member`的类型完全一致,包括是否带有`const`修饰符或其他限定符。例如,如果结构体中的成员是`struct my_struct *`,而传入的`ptr`是`void *`或者另一个类型的指针,就会导致类型不匹配。 另外,检查是否在调用`container_of`时,错误地传递了成员的偏移量或其他非指针类型的参数。确保第一个参数确实是指向结构体成员的指针,而不是其他类型的变量。 还有可能是在内核版本升级后,`container_of`的实现发生了变化,导致之前的代码不再兼容。这时候需要查看当前内核版本的`container_of`实现,并对照旧版本的代码进行调整。 此外,用户可能在使用`container_of`时,结构体成员的类型定义有误,或者在包含头文件时顺序有问题,导致类型未被正确识别。需要检查相关的头文件引用,确保所有相关的结构体定义都已正确包含。 最后,如果用户是在编写内核模块或驱动,可能需要检查Makefile是否正确配置,确保使用的内核头文件与当前编译的内核版本一致。内核头文件的不匹配也可能导致此类编译错误。 总结来说,解决这个问题的步骤包括: 1. 检查所有使用`container_of`的地方,确认参数类型是否匹配。 2. 确保结构体成员的类型定义正确,并与传入的指针类型一致。 3. 核对内核版本和头文件是否匹配,必要时调整代码以适应新版本的内核实现。 4. 使用调试手段,如打印类型信息或简化代码,逐步定位问题根源。</think>在编译Linux内核内核模块时遇到`container_of()`相关的类型不匹配错误,通常是因为代码中错误地使用了该。以下是逐步分析和解决方案: --- ### **错误原因分析** 错误信息: ```bash ./include/linux/compiler.h:577:38: error: call to ‘__compiletime_assert_390’ declared with attribute error: pointer type mismatch in container_of() ``` - **核心问题**:`container_of(ptr, type, member)`第一个参数`ptr`的类型与结构体`type`中成员`member`的类型不一致。 - **触发机制**:Linux内核在编译时会通过静态断言(`static_assert`)检查类型是否严格匹配,若不匹配则报错。 --- ### **步骤 1:理解`container_of`的工作原理** `container_of`用于通过结构体成员的指针反向获取整个结构体的指针。其典型实现如下: ```c #define container_of(ptr, type, member) ({ \ const typeof(((type *)0)->member) *__mptr = (ptr); \ (type *)((char *)__mptr - offsetof(type, member)); }) ``` - `ptr`:指向结构体成员的指针。 - `type`:结构体类型。 - `member`:结构体中成员的名称。 **关键点**:`ptr`的类型必须与`type`结构体中`member`的类型完全一致(包括`const`修饰符)。 --- ### **步骤 2:检查代码中`container_of`的使用** 在报错位置附近的代码中,找到所有`container_of`调用,并检查以下内容: 1. **参数类型一致性**: - 确保`ptr`的类型与结构体成员的类型完全匹配。 - 例如,如果结构体定义为: ```c struct my_struct { int value; struct list_head list; }; ``` 则`container_of`的调用应为: ```c struct my_struct *p = container_of(ptr, struct my_struct, list); ``` - `ptr`必须是`struct list_head *`类型。 2. **常见错误场景**: - `ptr`被错误地转换为`void *`或其他类型。 - 成员名称拼写错误(如`list`误写为`lst`)。 - 结构体类型名称错误(如`struct my_struct`误写为`my_struct_t`)。 --- ### **步骤 3:解决类型不匹配问题** #### **场景 1:`ptr`类型与成员类型不一致** - **错误示例**: ```c void *ptr = ...; struct my_struct *p = container_of(ptr, struct my_struct, list); // ptr类型为void* ``` - **修复方法**: ```c struct list_head *ptr = ...; // 确保ptr类型为struct list_head* struct my_struct *p = container_of(ptr, struct my_struct, list); ``` #### **场景 2:成员类型包含`const`修饰符** - **错误示例**: ```c const struct list_head *ptr = ...; struct my_struct *p = container_of(ptr, struct my_struct, list); // ptr是const指针 ``` - **修复方法**: ```c struct list_head *ptr = ...; // 移除const或匹配结构体成员类型 ``` #### **场景 3:内核版本升级导致的变化** - 如果代码原本正常,升级内核后报错,可能是`container_of`的静态检查更严格。 - 对比新旧内核头文件中`container_of`的实现,调整代码类型。 --- ### **步骤 4:验证修复** 1. 重新编译代码,确认错误是否消失。 2. 若仍有问题,使用`gcc -E`预编译查看展开后的代码: ```bash gcc -E -I./include your_file.c | less ``` 检查`container_of`展开后的类型赋值语句: ```c const typeof(((type *)0)->member) *__mptr = (ptr); ``` 若此处类型不匹配,需进一步调整`ptr`的类型。 --- ### **总结** - **核心原则**:`container_of`的第一个参数必须与结构体成员的类型严格一致。 - **调试技巧**:逐步注释代码、打印类型信息(`#pragma message`)、检查头文件包含顺序。 - **扩展阅读**:参考内核文档[`container_of`的正确用法](https://www.kernel.org/doc/html/latest/driver-api/basics.html)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值