內核鏈表是雙向循環鏈表;鏈表結點基本構成:數據、前向指針、後向指針;不一樣於傳統鏈表,前向指針和後向指針都是指向指針域沒有指向結點中的數據,想要讀取一個結點的數據是經過該結點的指針域拿到結點的數據;html
/*************************************************
*filename:mylist
*function:creat kernel list
*creater:dongry
*data:2019-04-02
*************************************************/
#include <linux/module.h> #include <linux/list.h> #include <linux/init.h> /*module statement*/ MODULE_LICENSE("GPL"); int a,b; extern int add(int a,int b); /*reload parameter a and b*/ module_param(a,int,S_IRUGO | S_IWUSR); module_param(b,int,S_IRUGO | S_IWUSR); /*defines a struct*/ struct information { int high; char *sex; char *name; struct list_head list; }; /*statement struct variable*/ struct information child1,child2,child3; struct information *tmp; struct list_head information_head; struct list_head *pos; static int mylist_init(void) { /*creat list and init list*/ INIT_LIST_HEAD(&information_head); child1.high=160; child1.sex="girl"; child1.name="lyf"; /*in list tail insert node*/ list_add_tail(&(child1.list),&(information_head)); child2.high=105; child2.sex="gay"; child2.name="cl"; /*in list top insert node*/ list_add(&(child2.list),&(information_head)); child3.high=175; child3.sex="girl"; child3.name="bbh"; /*in list tail insert node*/ list_add_tail(&(child3.list),&(information_head)); /*list ergodic*/ list_for_each(pos,&information_head) { /*get list pointer's node*/ tmp=list_entry(pos,struct information,list); /*printk srutct member*/ printk("High is %d,child sex is %s,Name is %s\n",tmp->high,tmp->sex,tmp->name); } return 0; } static void mylist_exit(void) { /*delete list node*/ list_del(&(child1.list)); list_del(&(child2.list)); list_del(&(child3.list)); } module_init(mylist_init); module_exit(mylist_exit);
1 分析遍歷鏈表與取出結點node
/*list ergodic*/
list_for_each(pos,&information_head) { /*get list pointer's node*/ tmp=list_entry(pos,struct information,list); /*printk srutct member*/ printk("High is %d,child sex is %s,Name is %s\n",tmp->high,tmp->sex,tmp->name); }
源代碼linux
#define list_for_each(pos, head) for(pos=(head)->next;prefetch(pos->next),pos!=(head);pos=pos->next) #define list_entry(ptr,type,member) container_of(ptr,type,member) #define container_of(ptr,type,member) \
( \
{ \
const typeof(((type*)0)->member)*__mptr=(ptr); \
(type*)((char*)__mptr - offsetof(type, member)); \
} \
)
1 關鍵字:typeof數組
1 將參數寫入typeof有兩種方式:表達式或類型;下面是一個使用表達式的例子。函數
typeof (x[0](1)) fetch
假設x是一個函數指針數組,就能夠到這個函數的返回值的類型了;spa
下面是一個類型名作參數的例子:指針
typeof(int *) m,n; 等價於 int *m,*n;code
2 若是將typeof用於表達式,則該表達式不會執行。只會獲得該表達式的類型。如下示例聲明瞭int類型的var變量,由於表達式func()是int類型的。因爲表達式不會被執行,因此不會調用func函數。orm
extern int func(int a,int b);
typeof(func()) var;
3 下面是兩個等效聲明,用於聲明int類型的變量a。
typeof(int) a; /*int類型*/
typeof('b') a; /* GCC中這個表達式的類型是int(自動提高爲int)。
注意typeof(char)和typeof('b')獲得的結果是不同的,能夠用sizeof()能夠看出來。
若是編寫頭文件,而且包含ISO C兼容的話,最好是用雙下劃線的形式:__typeof__;typeof和typedef很像,事實上,只要能用typedef的地方就能夠用typeof。
4 使用typeof的例子:
typeof (*x) y; /*把y定義成x指向的數據類型*/
typeof (*x) y[4]; /*把y定義成x指向數據類型的數組*/
typeof (typeof(char *)[4]) y; /*把y定義爲一個字符指針數組*/ 等價於 char *y[4];
5 宏定義使用例子:
#define pointer(T) typeof(T *)
#define array(T,N) typeof(T [N])
聲明能夠寫爲以下方式:
array (pointer(char),4) y; /*底線處:有4個指針的數組類型*/
6 使用typeof的聲明示例
typeof(int *) a,b; /* 聲明整型指針a, b */ 等價於 int *a,*b;
typeof(int) *a,b;/* 聲明整型指針a,整型b*/ 等價於 int *a,b;
typeof(int [10]) a1, a2;/* 聲明整型數組a,b */ 等價於 int a[10],b[10];
7 使用typeof的聲明限制
typeof構造中的類型名不能包含存儲類說明符,如extern或static。不過容許包含類型限定符,如const或volatile。例如,下列代碼是無效的,由於它在typeof構造中聲明瞭extern:
typeof(extern int) a; /*錯誤*/
使用外部連接來聲明標識符b是有效的,表示一個int類型的對象。
extern typeof(int) b;
聲明一個使用const限定字符的char類型指針,表示指針p不能被修改
typeof(char * const) p;
8 在宏聲明中使用typeof
typeof構造的主要應用是用在宏定義中。可使用typeof關鍵字來引用宏參數的類型。所以,在沒有將類型名明確指定爲宏實參的狀況下,構造帶所需類型的對象是能夠的。
下面是一個交換兩個變量的值的宏定義:
#define SWAP(a,b) {\
typeof(a) _t=a;\
a=b;\
b=_t;}
這個宏能夠交換全部基本數據類型的變量(整數,字符,結構等)
https://gcc.gnu.org/onlinedocs/gcc/Typeof.html
2 #define list_entry(ptr,type,member) container_of(ptr,type,member)
const typeof(((type*)0)->member)*__mptr=(ptr); 帶入參數
#define list_entry(pos,struct information,list) container_of(pos,struct information,list)
const typeof(((struct information*)0)->list)*__mptr=(pos);
以結構體struct information聲明的child1爲例,進行分析,假如child1的結構體成員內存地址以下(內存地址爲隨機編寫,沒有參考性)。
const typeof(((struct information*)0)->list)*__mptr=(pos);這句代碼的做用是聲明一個指針__mptr。typeof的做用是獲取變量的類型,因此__mptr的類型就是struct list_head(結合內核列表讀取結點的數據是經過結點的指針域,因此__mptr的類型要指向結點的指針域)。而後把pos的值賦給它,其實就是兩個指針指向同一塊內存。指向0x804a00c。
3 (type*)((char*)__mptr - offsetof(type, member))
offsetof源碼:#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) 帶入參數
#define offsetof(struct information, list) ((size_t) &((struct information *)0)->list)
(struct information*)((char*)__mptr - offsetof(struct information,list))
(struct information*)0)這代碼的意思是把information結構體的起始地址強制制定爲0,那麼list的地址就是相對0地址的偏移量,而後取址,在上圖中就是0x000000c。
(char)__mptr這句話的意思:把這個指針強制類型轉換爲(char),也就是這個指針如今是字節指針了。由於內存存儲的最基本單位都是字節。這樣的轉換能夠用來作加減法十分方便。
綜上所述,0x804a00c-0x000000c=0x804a000.這個地址也就是child1結構體的起始地址了,而後轉換爲struct information類型的指針。
4 分析#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER):