內核鏈表

內核鏈表是雙向循環鏈表;鏈表結點基本構成:數據、前向指針、後向指針;不一樣於傳統鏈表,前向指針和後向指針都是指向指針域沒有指向結點中的數據,想要讀取一個結點的數據是經過該結點的指針域拿到結點的數據;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)

  ((TYPE *)0) 將零轉型爲TYPE類型指針; 
  ((TYPE *)0)->MEMBER 訪問結構體中的成員; 
  &(((TYPE *)0)->MEMBER )取出成員的地址; 這個實現至關於獲取到了MEMBER成員相對於其所在結構體的偏移,也就是其在對應結構體中的什麼位置。
  (size_t)(&(((TYPE*)0)->MEMBER))結果轉換類型。巧妙之處在於將0轉換成(TYPE*),結構之內存空間首地址0做爲起始地址,則成員地址天然爲偏移地址;
       &操做若是是對一個表達式,而不是一個標識符,會取消操做,而不是添加。好比&*a,會直接把a的地址求出來,不會訪問*a;&a->member,會把訪問a->member的操做取消,只會計算出a->member的地址。
相關文章
相關標籤/搜索