container_of 是 Linux 核心最精妙、最高頻使用的巨集之一,用於從結構體成員指標反推結構體起始位址。本文完整拆解巨集定義、typeof/offsetof 關鍵技術、記憶體佈局圖解、逐行程式碼展開分析,並給出字元裝置驅動中 open() 實作的實戰範例。
#define container_of(ptr, type, member) ({ \
const typeof(((type *)0)->member) *__mptr = (ptr); \
(type *)((char *)__mptr - offsetof(type, member)); \
})
ptr:成員指標type:結構體類型member:成員名稱(非類型)取得運算元類型。typeof(((type *)0)->member) 取得 member 的類型。
#define offsetof(TYPE, MEMBER) ((size_t)&(((TYPE *)0)->MEMBER))
將 0 轉為 TYPE*,訪問 MEMBER 並取址 → 相對結構體起始位址的位元組偏移。
假設結構體:
struct demo_struct {
int member1; // offset 0
int member2; // offset 4
int member3; // offset 8
int member4; // offset 12
};
struct demo_struct demo = {1,2,3,4}; // 位於 0xA000
int *memp = &demo.member3; // 0xA008
呼叫 container_of(memp, struct demo_struct, member3) 展開:
{
const typeof(((struct demo_struct *)0)->member3) *__mptr = memp; // __mptr = 0xA008
(struct demo_struct *)((char *)__mptr - offsetof(struct demo_struct, member3));
// offsetof = 8
// (char *)0xA008 - 8 = 0xA000
// 轉型為 struct demo_struct * → 0xA000
}
結果精確指向 demo 起始位址。
struct globalmem_dev {
struct cdev my_cdev; // 字元裝置基礎結構
unsigned char mem[GLOBALMEM_SIZE];
struct semaphore sem;
};
struct globalmem_dev *pdev;
int globalmem_open(struct inode *inode, struct file *filp) {
// inode->i_cdev 指向嵌入在 globalmem_dev 中的 cdev
pdev = container_of(inode->i_cdev, struct globalmem_dev, my_cdev);
filp->private_data = pdev; // 供後續 read/write/ioctl 使用
if (down_trylock(&pdev->sem)) // 非阻塞獲取信號量
return -EBUSY;
return 0;
}
關鍵點:
inode->i_cdev 是 VFS 層指向 cdev 的指標my_cdev 是 globalmem_dev 的成員名稱,非類型filp->private_datacontainer_of 回傳的是結構體指標,需正確接收cdev、kobject、list_head)typeof),比手動位移運算安全