Windows XP Windows 7 Windows 2003 Windows Vista Windows 綜合教學 Linux 系統教學
Windows 10 Windows 8 Windows 2008 Windows NT Windows Server 電腦軟體教學
您现在的位置: Windows教程網 >> Windows教程 >  >> Linux 系統教學 >> Linux 教學

Linux 核心 container_of 巨集原理深度解析與實戰應用

編輯:Linux 教學

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:成員名稱(非類型)

二、核心技術拆解

1. typeof() — GNU C 延伸

取得運算元類型。typeof(((type *)0)->member) 取得 member 的類型。

2. offsetof() — 計算成員偏移


#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 起始位址。

四、實戰應用:字元裝置驅動 open()


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_data

五、常見錯誤提醒

  • 第三參數傳類型名而非成員名 → 編譯錯誤或運行錯誤
  • 忘記 container_of 回傳的是結構體指標,需正確接收
  • 書本《Linux 裝置驅動開發詳解》P132 參數說明有誤,需以核心原始碼為準

六、為什麼核心到處使用 container_of?

  • 避免在結構體中額外儲存「父結構體指標」,節省記憶體
  • 支援多重嵌入:同一結構體可同時掛載到多個子系統(如 cdev、kobject、list_head)
  • 編譯期類型檢查(typeof),比手動位移運算安全
Copyright © Windows教程網 All Rights Reserved