# 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進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。