前言
在linux 核心中,container_of 函數使用非常廣,例如 linux核心鏈表 list_head、工作隊列work_struct中。
在linux核心中大名鼎鼎的宏container_of() ,其實它的文法很簡單,只是一些指標的靈活應用,它分兩步:
第一步,首先定義一個臨時的資料類型(通過typeof( ((type *)0)->member )獲得)與ptr相同的指標變數__mptr,然後用它來儲存ptr的值。
第二步,用(char *)__mptr減去member在結構體中的位移量,得到的值就是整個結構體變數的首地址(整個宏的傳回值就是這個首地址)。
那這個宏到底是用來幹嘛的呢?我們先來看看它在核心中是怎樣定義的。
呵呵,乍一看不知道是什麼東東。
我們先來分析一下container_of(ptr,type,member) ,這裡面有ptr,type,member分別代表指標、類型、成員。
看一個例子:
Struct test { int i; int j; char k; }; Struct test temp;
現在呢如果我想通過temp.j的地址找到temp的首地址就可以使用container_of(&temp.j,struct test,j);
現在我們知道container_of()的作用就是通過一個結構變數中一個成員的地址找到這個結構體變數的首地址。
下面來看看比較複雜的內容:
我們用上面的struct test張展一下
Const typeof(((struct test *)0)->j) * __mptr = (&temp.j); //(sturct test *)0 表示資料區段基址
其中,typeof是GNU C對標準C的擴充,它的作用是根據變數擷取變數的類型。因此,上述代碼的作用是首先使用typeof擷取結構體成員j的類型為int,然後頂一個int指標類型的臨時變數__mptr,並將結構體變數中的成員的地址賦給臨時變數__mptr。
(struct test *)((char *)__mptr - offsetof(struct test,j));
接著我們來看一下offsetof(struct test,j) ,他在核心中如下定義
展開(size_t)&((struct test *)0)->j ,這是什麼東東?
一開始也不明白,這裡要感謝曹忠明老師的熱心協助,一語驚醒夢中人,呵呵,可以是這樣理解。
其中size_t是整型,那麼我們可以知道最終的結果是一個整形值,也就是j相對於0地址的位移量。也許現在你會問,整出這麼個玩意幹嘛,下面看個列子:
程式運行結果:
發現沒有如果把第二個值 減去最後一個值,就能得到第一個值。
在回首一下它:
(struct test *)((char *)__mptr - offsetof(struct test,j));
是不是可以獲得結構體變數temp的首地址呀,是不是太精妙了呀
總結
linux核心中隨隨便便一個宏就有如此精妙,想想對linux瞭解非常多的牛人,還有很長一段路。好了,以上就是本文的全部內容了,希望這篇文章的內容對大家的工作和學習能有所協助,如果有疑問可以留言交流,謝謝大家對雲棲社區的支援。