2010年1月25日 星期一

Linux Kernel: container_of 巨集

Linux驅動程式裡很常看container_of巨集,其目的為何呢? 假設Linux驅動程式只知道某一結構成員的位址,該驅動程式便可使用container_of巨集,將已知某一結構成員的位址計算出該結構的起始位址,其原型如下:


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



假設有一結構定義如下,且我們只知道phone_num成員的位址,如此便能使用該巨集計算出該結構變數的起始位址:


/* The student structure definition */
typedef struct student {
     char name[16];
     int id;
     char addr[64];
     char phone_num[16];
}STUDENT, *STUDENT_PTR;


該結構記憶體配置圖如下所示:


Figure 1. 結構記憶體配置圖

範例程式碼:

Figure 2. 範例程式碼


以此結構為範例來看containter_of巨集的兩行程式碼:

◎ const typeof( ((type *)0)->member ) *__mptr = (ptr); ==> 得到phone_num的位址 (x+84) ,請參考Figure 1跟Figure 2。
◎ (type *)( (char *)__mptr - offsetof(type,member) ); → (type *)( (char *)__mptr - ((size_t) &((type *)0)->member) ); ==> 其中((size_t) &((type *)0)->member)這段敘述代表以零為起始位址算出member這個成員的相對位址,即為phone_num成員的相對位址,也就是84 (請參考Figure 1)。所以整個敘述變成 (x+84) - 84 = x ,如此便能取得該結構變數的起始位址。


範例程式

3 則留言:

CK 提到...

謝謝~~很清楚

Adrian Huang (黃圳柏) 提到...

感謝您的支持!

Grace Kao 提到...

gcc會產生warning
in "print_id" function
warning: initialization from incompatible pointer type