linux核心常用巨集 container_of
阿新 • • 發佈:2019-01-12
在linux 核心中有一些騷操作的巨集,以下是我對container_of巨集的理解
舉個例子假設有如下結構體
struct test {
int a;
int b,
int c;
} *test1;
一般來說,我有test結構體變數的指標test1,我可以用test->a,test->b,test->c來分別訪問變數a,b,c.
假設我現在知道b變數的地址(指標),能不能找到test結構體變數母體的地址呢?
container_of巨集就是來解決這個問題的,用法如下
#define offsetof(struct_t,member) ((size_t)(char *)&((struct_t *)0)->member) #define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );}) stuct test *result; int *test_b = &test1->b; result = container_of(test_b,struct test,b);
result 就是我們獲得的struct test的地址,其實與test1是同一個值。
下面解析container_of這個巨集
這麼騷的操作的實現其實也只有兩行程式碼哈哈,他主要依賴於gcc c編譯器的內建關鍵字typeof,和offsetof這個巨集
我們就具體問題具體分析對以下這一句進行巨集展開
result = container_of(test_b,struct test,b);
具體分析,首先是第一行程式碼的解析
const typeof( ((type *)0)->member ) *__mptr = (ptr); \\這裡巨集展開為 const typeof( ((struct test *)0)->b) *__mptr = test_b; \\進一步展開為 const int *__mptr = test_b;
typeof其實就是獲取 結構體成員b的型別,這裡為int型,這句程式碼只是定義了一個變數__mptr,將test_b的值賦予它。當然__mptr的型別和test_b的型別是一致的,都為int *型。
然後是第二行程式碼的解析
(type *)( (char *)__mptr - offsetof(type,member) ); \\這裡巨集展開為 (type *)( (char *)__mptr - ((size_t)(char *)&(type *)0->member) ); \\ 進一步展開為 (struct test *)( (char *)__mptr - ((size_t)(char *)&(struct test *)0->b) ); \\ 進一步計算為 (struct test *)( (char *)__mptr - ((size_t)(char *)(4) ); \\ 進一步計算為 (struct test *)( (char *)__mptr - 4 ); \\ 進一步計算為 (struct test *)( (char *)test_b - 4 );
其實最後結果就是test_b的值減去4,然後再強制轉換為struct test *型的指標而已。
比較trick的實現其實只是offsetof這個巨集, 在此處的例子中,0這個地址強制轉換為strcut test*的指標,然後訪問其變數b,再獲取b變數的地址,再其強制轉換為無符號整型size_t.
因為這個struct test結構體地址從0開始,所以b變數的地址就是0+4,這個值了。
這個看似複雜的東西其實其本質都還是挺簡單的。