linux 2.6.36代码构架,Linux 内核笔记(2.6.36)(二)
C語言基礎
鏈表
linux內核代碼中大量使用鏈表,為了提高效率,內核采用了一套通用的,一般的,可以用到各種不同數據結構的隊列操作。在include/linux/ list.h中,有如下申明:
點擊(此處)折疊或打開
struct list_head {
struct list_head *next, *prev;
};
于是,其余數據結構如inode等將其引用而作為一個成員,就可以完成下面將要講到的若干操作而不去單獨定義了。引用該數據結構的結構我們稱為宿主結構。
鏈表的插入,刪除和查找不再贅述,這里主要介紹一種十分巧妙并且在內核中經常用到的鏈表操作--返回宿主結構。
如果需要有某種數據結構的隊列,就在這種數據結構定義內部放上一個list_head數據結構。
例如,建立數據結構foo鏈表的方式是,在foo的定義中,嵌入了一個list_head成員list。
這里foo就是所指的"宿主":
點擊(此處)折疊或打開
typedef struct foo {
…
struct list_head list;
…
};
但是,如何通過list_head成員訪問到宿主結構項呢?畢竟list_head不過是個連接件,而我們需要的是一個"特定"的數據結構鏈表。先介紹幾個基本宏:offsetof、typeof、containerof。
offsetof定義在include/linux/stddef.h
點擊(此處)折疊或打開
#undef offsetof
#ifdef __compiler_offsetof
#define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER)
#else
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#endif
#endif /* __KERNEL__ */
其中__compiler_offsetof(a,b)的定義在include/linux/compiler-gcc4.h
點擊(此處)折疊或打開
#define __compiler_offsetof(a,b) __builtin_offsetof(a,b)
而__builtin_offsetof()宏就是在編譯器中已經設計好了的函數,直接調用即可。
解釋offsetof:一共4步
1. ( (TYPE *)0 ) 強制 "轉換" 為 TYPE結構的指針;
2. ((TYPE *)0)->MEMBER 訪問結構中的數據成員;
3. &( ( (TYPE *)0 )->MEMBER)取出數據成員的地址;
4. (size_t)(&(((TYPE*)0)->MEMBER))結果轉換類型.巧妙之處在于將0轉換成(TYPE*),結構以內存空間首地址0作為起始地址,則成員地址自然為偏移地址;
這里使用的是一個利用編譯器技術的小技巧(編譯器自動算出成員的偏移量),即先求得結構成員變量在結構體中的相對于結構體的首地址的偏移地址,然后根據結構體的首地址為0,從而得出該偏移地址就是該結構體變量在該結構體中的偏移,即:該結構體成員變量距離結構體首的距離。在offsetof()中,這個
member成員的地址實際上就是type數據結構中member成員相對于結構變量的偏移量。對于給定一個結
構,offsetof(type,member)是一個常量,list_entry()正是利用這個不變的偏移量來求得鏈表數據項的變量地址。
typeof的作用可以簡單解釋為提取變量類型,類似于sizeof(取變量大小),舉例如下:
點擊(此處)折疊或打開
unsigned int i;
typeof(i) x;
x=100;
printf("x:%d/n",x);
代碼中的typeof(i)等效于unsigned int。
container_of的定義在include/linux/kernel.h
點擊(此處)折疊或打開
/**
* container_of - cast a member of a structure out to the containing structure
* @ptr:????the pointer to the member.
* @type:????the type of the container struct this is embedded in.
* @member:????the name of the member within the struct.
*
*/
#define container_of(ptr, type, member) ({????????????\
const typeof( ((type *)0)->member ) *__mptr = (ptr);????\
(type *)( (char *)__mptr - offsetof(type,member) );})
分析:
1.(type
*)0->member為設計一個type類型的結構體,起始地址為0,編譯器將結構體的起始的地址加上此結構體成員變量的偏移得到此結構體成員變
量的地址,由于結構體起始地址為0,所以此結構體成員變量的偏移地址就等于其成員變量在結構體內距離結構體開始部分的偏移量。即:&(type
*)0->member就是取出其成員變量的偏移地址。而其等于其在結構體內的偏移量:即為:(size_t)(& ((type
*)0)->member)經過size_t的強制類型轉換后,其數值為結構體內的偏移量。該偏移量這里由offsetof()求出。
2.typeof( ( (type *)0)->member )為取出member成員的變量類型。用其定義__mptr指針;ptr為指向該成員變量的指針。__mptr為member數據類型的常量指針,其指向ptr所指向的變量處。
3.(char
*)__mptr轉換為字節型指針。(char *)__mptr - offsetof(type,member)
)用來求出結構體起始地址(為char *型指針),然后(type *)( (char *)__mptr -
offsetof(type,member) )在(type *)作用下進行將字節型的結構體起始指針轉換為type *型的結構體起始指針。
這就是從結構體某成員變量指針來求出該結構體的首指針。指針類型從結構體某成員變量類型轉換為該結構體類型。
介紹了上面的幾種基本宏后,對list_entry的理解就容易了。
list_entry()宏,獲取當前list_head鏈表節點所在的宿主結構項,定義在include/linux/list.h。
第一個參數為當前list_head節點的指針,即指向宿主結構項的list_head成員。
第二個參數是宿主數據結構的定義類型。
第三個參數為宿主結構類型定義中list_head成員名。
點擊(此處)折疊或打開
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
擴展替換為:
點擊(此處)折疊或打開
#define list_entry(ptr, type, member) \
((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
另外由list_entry()引申的函數有list_first_entry,list_for_each_entry,list_for_each_entry_reverse等都不再贅述。
總結
以上是生活随笔為你收集整理的linux 2.6.36代码构架,Linux 内核笔记(2.6.36)(二)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux libfcmain.so,B
- 下一篇: linux history nginx,