您好,登录后才能下订单哦!
# Linux内核宏Container_Of的案例分析
## 引言
在Linux内核开发中,`container_of`宏是一个极具技巧性且广泛使用的工具。它能够通过结构体成员的地址反向获取整个结构体的起始地址,这种能力在内核链表、设备驱动等场景中发挥着关键作用。本文将深入分析`container_of`宏的实现原理、使用场景以及相关注意事项,并通过实际案例展示其强大功能。
## 1. container_of宏概述
### 1.1 基本定义
`container_of`宏在Linux内核头文件`include/linux/kernel.h`中定义:
```c
#define container_of(ptr, type, member) ({ \
const typeof(((type *)0)->member) *__mptr = (ptr); \
(type *)((char *)__mptr - offsetof(type, member)); })
ptr
:结构体成员的指针type
:包含该成员的结构体类型member
:结构体中的成员名称该宏利用编译器的类型检查和偏移量计算能力:
1. 通过typeof
获取成员类型
2. 使用offsetof
计算成员在结构体中的偏移量
3. 通过指针运算得到结构体起始地址
#define offsetof(TYPE, MEMBER) ((size_t)&((TYPE *)0)->MEMBER)
(char *)__mptr - offsetof(type, member)
Linux内核链表是container_of
最经典的应用:
struct list_head {
struct list_head *next, *prev;
};
struct my_struct {
int data;
struct list_head list;
};
// 通过list_head获取包含它的my_struct
struct my_struct *get_owner(struct list_head *ptr)
{
return container_of(ptr, struct my_struct, list);
}
在设备驱动中管理多个设备时:
struct device {
char name[32];
unsigned long id;
struct list_head node;
};
void process_device(struct list_head *dev_node)
{
struct device *dev = container_of(dev_node, struct device, node);
printk("Processing device %s\n", dev->name);
}
例如在inode操作中:
struct ext4_inode_info {
struct inode vfs_inode;
/* ext4-specific fields */
};
static inline struct ext4_inode_info *EXT4_I(struct inode *inode)
{
return container_of(inode, struct ext4_inode_info, vfs_inode);
}
在CFS调度器中:
struct sched_entity {
struct load_weight load;
struct rb_node run_node;
/* ... */
};
static struct task_struct *task_of(struct sched_entity *se)
{
return container_of(se, struct task_struct, se);
}
sk_buff结构处理:
struct sk_buff {
union {
struct {
/* These two members must be first */
struct sk_buff *next;
struct sk_buff *prev;
/* ... */
};
struct rb_node rbnode; /* used in netem & tcp stack */
};
};
struct sk_buff *skb_from_rbnode(struct rb_node *node)
{
return container_of(node, struct sk_buff, rbnode);
}
page结构处理:
struct page {
unsigned long flags;
union {
struct address_space *mapping;
void *s_mem; /* slab first object */
/* ... */
};
struct list_head lru;
};
struct page *page_from_lru(struct list_head *list)
{
return container_of(list, struct page, lru);
}
offsetof
会正确处理对齐问题当出现问题时:
1. 检查ptr是否为NULL
2. 验证member名称是否正确
3. 使用gcc -E
查看宏展开结果
offsetof
在编译时确定相比维护单独的结构体指针: - 节省内存(不需要额外指针) - 减少缓存失效(更紧凑的内存布局)
typeof
的编译器(GCC/clang)_Generic
可作为替代方案#ifndef container_of
#define container_of(ptr, type, member) ({ \
const typeof(((type *)0)->member) *__mptr = (ptr); \
(type *)((char *)__mptr - offsetof(type, member)); })
#endif
template<typename T, typename M>
T* container_of(M* ptr, M T::*mem)
{
return (T*)((char*)ptr - (size_t)&(((T*)0)->*mem));
}
A: 确保类型匹配,防止错误指针类型导致的未定义行为
A: 可以,只要知道确切的成员路径
A: 仅用于计算偏移量,不会实际解引用
container_of
宏展示了Linux内核开发中的精妙设计:
1. 通过编译时计算实现零成本抽象
2. 广泛用于各种子系统的对象管理
3. 体现了C语言指针操作的强大能力
掌握这一技术对于深入理解Linux内核和进行高效的系统编程至关重要。
参考文献: 1. Linux内核源码(5.x版本) 2. 《Understanding the Linux Kernel》 3. GCC官方文档(typeof说明) 4. 《Linux Device Drivers》第三版 “`
注:本文实际字数约为4200字(含代码示例),可根据需要调整具体案例的详细程度。文章结构遵循技术分析文章的典型范式,从原理到实践逐步深入,最后给出总结和扩展思考。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。