日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

[转]深入理解linux内核list_head

發布時間:2023/12/18 linux 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [转]深入理解linux内核list_head 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

http://blog.chinaunix.net/uid-27122224-id-3277511.html
深入理解linux內核list_head的實現 2012-07-17 17:37:01

分類: LINUX

前言:在linux源代碼中有個頭文件為list.h。很多linux下的源代碼都會使用這個頭文件,它里面定義

了一個結構,以及定義了和其相關的一組函數,這個結構是這樣的:

點擊(此處)折疊或打開

1.struct list_head{

  • struct list_head *next, *prev;
  • 3.};

    那么這個頭文件又是有什么樣的作用呢,這篇文章就是用來解釋它的作用,雖然這是linux下的源代碼,但對

    于學習C語言的人來說,這是算法和平臺沒有什么關系。

    一、雙向鏈表

    學習計算機的人都會開一門課程《數據結構》,里面都會有講解雙向鏈表的內容。

    什么是雙向鏈表,它看起來是這樣的:

    點擊(此處)折疊或打開

    1.struct dlist

    2.{

  • int no;
  • void* data;
  • struct dlist *prev, *next;
  • 6.};
    他的圖形結構圖:

    現在有幾個結構體,它們是:

    表示人的:

    點擊(此處)折疊或打開

    1.struct person

    2.{

  • int age;
  • int weight;
  • 5.};

    表示動物的:

    點擊(此處)折疊或打開

    1.struct animal

    2.{

  • int age;
  • int weight;
  • 5.};

    如果有一組filename變量和filedata變量,把它們存起來,我們會怎么做,當然就用數組了,但我們想使

    用雙向鏈表,讓它們鏈接起來,那該怎么做,唯一可以做的就是給每個結構加如兩個成員,如下:

    表示人的:

    點擊(此處)折疊或打開

    1.struct person

    2.{

  • int age;
  • int weight;
  • struct person *next, *prev;
  • 6.};
    表示動物的:

    點擊(此處)折疊或打開

    1.struct animal

    2.{

  • int age;
  • int weight;
  • struct animal *next, *prev;
  • 6.};

    現在有一個人的一個鏈表的鏈頭指針person_head (循環雙向鏈表)和動物的鏈表的鏈頭指針

    ainimal_head ,我們要獲得特定年齡和特定體重的人或動物(假設不考慮重疊),那么代碼看起來可能是這樣:

    點擊(此處)折疊或打開

    1.struct person * get_percent(int age, int weight)

    2.{

    3.…....

  • struct person *p;
  • for(p = person_head->next; p != person_head; p=p->next)
  • {
  • if(p->age == age && p->weight == weight)
  • return p;
  • }
  • 10.…...

    11.}

    那同理,要獲得一個特定年齡和重量的動物的函數get_animal(int age, int weight)的代碼也是和上面

    的類似。

    我們再回過頭來看這兩個結構,它們的指向前和指向后的指針其實都差不多,那把它們綜合起來吧,所以看起

    來如下面:

    點擊(此處)折疊或打開

    1.struct list_head{

  • struct list_head *next, *prev;
  • 3.};
    表示人的:

    點擊(此處)折疊或打開

    1.struct person{

  • int age;
  • int weight;
  • struct list_head list;
  • 5.};
    動物的:

    點擊(此處)折疊或打開

    1.struct animal

    2.{

  • int age;
  • int weight;
  • struct list_head list;
  • 6.};

    可能又會有些人會問了,struct list_head都不是struct persion和struct animal類型,怎么可以

    做鏈表的指針呢?其實,無論是什么樣的指針,它的大小都是一樣的,32位的系統中,指針的大小都是32位

    (即4個字節),只是不同類型的指針在解釋的時候不一樣而已,那么這個struct list_head又是怎么去

    做這些結構的鏈表指針呢,那么就請看下一節吧:)。

    二、struct list_head結構的操作

    首先,讓我們來看下和struct list_head有關的兩個宏,它們定義在list.h文件中。

    點擊(此處)折疊或打開

    1.#define LIST_HEAD_INIT(name) { &(name), &(name) }

    2.#define LIST_HEAD(name) struct list_head name = LIST_HEAD_INIT(name)

    3.#define INIT_LIST_HEAD(ptr) do { ?

  • (ptr)->next = (ptr); (ptr)->prev = (ptr); \
  • 5.} while (0)
    這兩個宏是用了定義雙向鏈表的頭節點的,定義一個雙向鏈表的頭節點,我們可以這樣:

    點擊(此處)折疊或打開

    1.struct list_head head;

    2.LIST_HEAD_INIT(head);
    又或者直接這樣:

    點擊(此處)折疊或打開

    1.LIST_HEAD(head);

    這樣,我們就定義并初始化了一個頭節點。

    點擊(此處)折疊或打開

    1.#define LIST_HEAD_INIT(name) { &(name), &(name) }

    就是用head的地址初始化其兩個成員next和prev ,使其都指向自己。

    我們再看下和其相關的幾個函數,這些函數都作為內聯函數也都定義list.h中,這里要說明一下linux源碼

    的一個風格,在下面的這些函數中以下劃線開始的函數是給內部調用的函數,而以符開始的函數就是對外使用

    的函數,這些函數一般都是調用以下劃線開始的函數,或是說是對下劃線開始的函數的封裝。

    2.1 增加節點的函數

    點擊(此處)折疊或打開

    1.static inline void __list_add();

    2.static inline void list_add();

    3.static inline void list_add_tail();
    其實看源代碼是最好的講解了,這里我再簡單的講一下。

    點擊(此處)折疊或打開

    1./**

    • __list_add - Insert a new entry between two known consecutive entries.
    • @new:
    • @prev:
    • @next:
    • This is only for internal list manipulation where we know the prev/next
    • entries
  • */
  • 10.static inline void __list_add(struct list_head * new,

  • struct list_head * prev, struct list_head * next)
  • 12.{

  • next->prev = new;
  • new->next = next;
  • new->prev = prev;
  • prev->next = new;
  • 17.}

    18.//這個函數在prev和next間插入一個節點new。

  • 20./**

    • list_add - add a new entry
    • @new: new entry to be added
    • @head: list head to add it after
    • Insert a new entry after the specified head.
    • This is good for implementing stacks.
  • */
  • 點擊(此處)折疊或打開

    1.static inline void list_add(struct list_head new, struct list_head head)

    2.{

  • __list_add(new, head, head->next);
  • 4.}

    5.//這個函數在head節點后面插入new節點。

  • 7./**

    • list_add_tail - add a new entry
    • @new: new entry to be added
    • @head: list head to add it before
    • Insert a new entry before the specified head.
    • This is useful for implementing queues.
  • */
  • 15.static inline void list_add_tail(struct list_head new, struct list_head head)

    16.{

  • __list_add(new, head->prev, head); 18.}
    這個函數和上面的那個函數相反,它在head節點的前面插入new節點。
  • 2.2 從鏈表中刪除節點的函數

    點擊(此處)折疊或打開

    1./**

    • __list_del -
    • @prev:
    • @next:
    • Delete a list entry by making the prev/next entries point to each other.
    • This is only for internal list manipulation where we know the prev/next
    • entries
  • */
  • 11.static inline void __list_del(struct list_head * prev,

  • struct list_head * next)
  • 13.{

  • next->prev = prev;
  • prev->next = next;
  • 16.}

  • 18./**

    • list_del - deletes entry from list.
    • @entry: the element to delete from the list.
      • Note: list_empty on entry does not return true after this, the entry is in
    • an undefined state.
  • */
  • 24.static inline void list_del(struct list_head *entry)

    25.{

  • __list_del(entry->prev, entry->next);
  • 27.}

  • 29./**

    • list_del_init - deletes entry from list and reinitialize it.
    • @entry: the element to delete from the list.
  • */
  • 33.static inline void list_del_init(struct list_head *entry)

    34.{

  • __list_del(entry->prev, entry->next);
  • INIT_LIST_HEAD(entry);
  • 37.}

    這里簡單說一下,list_del(struct list_head *entry)是從鏈表中刪除entry節點。

    list_del_init(struct list_head *entry) 不但從鏈表中刪除節點,還把這個節點的向前向后指針都指

    向自己,即初始化。

    那么,我們怎么判斷這個鏈表是不是空的呢!上面我說了,這里的雙向鏈表都是有一個頭節點,而我們上面看到,定義一個頭節點時我們就初始化了,即它的prev和next指針都指向自己。所以這個函數是這樣的。

    點擊(此處)折疊或打開

    1./**

    • list_empty - tests whether a list is empty
    • @head: the list to test.
  • */
  • 5.static inline int list_empty(struct list_head *head)

    6.{

  • return head->next == head;
  • 8.}

    講了這幾個函數后,這又到了關鍵了,下面講解的一個宏的定義就是對第一節中,我們所要說的為什么在一個

    結構中加入struct list_head變量就把這個結構變成了雙向鏈表呢,這其中的關鍵就是怎么通過這個

    struct list_head變量來獲取整個結構的變量,下面這個宏就為你解開答案:

    點擊(此處)折疊或打開

    1./**

    • list_entry - get the struct for this entry
    • @ptr: the &struct list_head pointer.
    • @type: the type of the struct this is embedded in.
    • @member: the name of the list_struct within the struct.
  • */
  • 7.#define list_entry(ptr, type, member) ?

  • ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
  • 乍一看下,不知道這個宏在說什么,沒關系,我舉個例子來為你一一解答 :)

    首先,我們還是用上面的結構:

    點擊(此處)折疊或打開

    1.struct person

    2.{

  • int age;
  • int weight;
  • struct list_head list;
  • 6.};

    我們一看到這樣的結構就應該知道它定義了一個雙向鏈表,下面來看下。

    我們有一個指針:

    struct list_head *pos;

    現在有這個指針,我們怎么去獲得這個指針所在的結構的變量(即是struct person變量,其實是struct

    person指針)呢?看下面這樣使用:

    點擊(此處)折疊或打開

    1.struct person *one = list_entry(pos, struct person, list);
    不明白是吧,展開一下 list_entry結構如下:

    點擊(此處)折疊或打開

    1.((struct person )((char )(pos) - (unsigned long)(&((struct person *)0)->list)))

    我慢慢的分解,首先分成兩部分(char )(pos)減去(unsigned long)(&((struct person )0)-

    list)然后轉 成(struct person *)類型的指針。

    (char )(pos):是將pos由struct list_head轉 成char* ,這個好理解。

    (unsigned long)(&((struct person )0)->list):先看最里面的(struct person )0),它是把0

    地址轉 成struct person指針,然后(struct person *)0)->list就是指向list變量,之后是

    &((struct person *)0)->list是取這個變量的地址,最后是(unsigned long)(&((struct person

    *)0)->list)把這個變量的地址值變成一個整形數!

    這么復雜啊,其實說白了,這個(unsigned long)(&((struct person *)0)->list)的意思就是取list

    變量在struct person結構中的偏移量。

    用個圖形來說(unsigned long)(&((struct person *)0)->list,如下:

    而(unsigned long)(&((struct person *)0)->list就是獲取這個offset的值。

    ((char )(pos) - (unsigned long)(&((struct person )0)->list))

    就是將pos指針往前移動offset位置,即是本來pos是struct list_head類型,它即是list。即是把

    pos指針往struct person結構的頭地址位置移動過去,如上圖的pos和虛箭頭。

    當pos移到struct person結構頭后就轉 成(struct person *)指針,這樣就可以得到struct person

    *變量了。

    所以我們再回到前面的句子

    點擊(此處)折疊或打開

    1.struct person *one = list_entry(pos, struct person, list);

    2.//就是由pos得到pos所在的結構的指針,動物就可以這樣:

    3.struct animal *one = list_entry(pos, struct animal, list);
    下面我們再來看下和struct list_head相關的最后一個宏。
    2.3 list_head 的遍歷的宏

    點擊(此處)折疊或打開

    1./**

    • list_for_each - iterate over a list
    • @pos: the &struct list_head to use as a loop counter.
    • @head: the head for your list.
  • */
  • 6.#define list_for_each(pos, head) ?

  • for (pos = (head)->next; pos != (head); pos = pos->next)
  • 9./**

    • list_for_each_safe - iterate over a list safe against removal of list entry
    • @pos: the &struct list_head to use as a loop counter.
    • @n: another &struct list_head to use as temporary storage
    • @head: the head for your list.
  • */
  • 15.#define list_for_each_safe(pos, n, head) ?

  • for (pos = (head)->next, n = pos->next; pos != (head); \
  • pos = n, n = pos->next)
  • list_for_each(pos, head)是遍歷整個head鏈表中的每個元素,每個元素都用pos指向。

    list_for_each_safe(pos, n, head)是用于刪除鏈表head中的元素,不是上面有刪除鏈表元素的函數了

    嗎,為什么這里又要定義一個這樣的宏呢??聪逻@個宏后面有個safe字,就是說用這個宏來刪除是安全的,

    直接用前面的那些刪除函數是不安全的。這個怎么說呢,我們看下下面這個圖,有三個元素a ,b ,c。

    點擊(此處)折疊或打開

    1.list_for_each(pos, myhead)

    2.{

  • if (pos == b)
  • list_del_init(pos);
  • //break;
  • }
  • 。。。
  • 13.}
    上面的算法是不安全的,因為當我們刪除b后,如下圖這樣:

    上刪除pos即b后,list_for_each要移到下一個元素,還需要用pos來取得下一個元素,但pos的指向已

    經改變,如果不直接退出而是在繼續操作的話,就會出錯了。

    而 list_for_each_safe就不一樣了,如果上面的代碼改成這樣:

    點擊(此處)折疊或打開

    1.struct list_head pos, n;

    2.list_for_each_safe(pos, n, myhead)

    3.{

  • if (pos == b)
  • list_del_init(pos);
  • //break;
  • }
  • 。。。
  • 14.}

    這里我們使用了n作為一個臨時的指針,當pos被刪除后,還可以用n來獲得下一個元素的位置。

    說了那么多關于list_head的東西,下面應該總結一下,總結一下第一節想要解決的問題.

    三、 總例

    我用一個程序來說明在struct person中增加了struct list_head變量后怎么來操作這樣的雙向鏈表。

    點擊(此處)折疊或打開

    1.#include <stdio.h>

    2.#include "list.h"

  • 4.struct person

    5.{

  • int age;
  • int weight;
  • struct list_head list;
  • 10.};

  • 12.int main(int argc, char* argv[])

    13.{

  • struct person *tmp;
  • struct list_head *pos, *n;
  • int age_i, weight_j;
  • // 定義并初始化一個鏈表頭
  • struct person person_head;
  • INIT_LIST_HEAD(&person_head.list);
  • for(age_i = 10, weight_j = 35; age_i < 40; age_i += 5, weight_j += 5)
  • {
  • tmp =(struct person*)malloc(sizeof(struct person));
  • tmp->age = age_i;
  • tmp->weight = weight_j;
  • // 把這個節點鏈接到鏈表后面
  • // 這里因為每次的節點都是加在person_head的后面,所以先加進來的節點就在鏈表里的最
  • 30.后面

  • // 打印的時候看到的順序就是先加進來的就在最后面打印
  • list_add(&(tmp->list), &(person_head.list));
  • }
  • // 下面把這個鏈表中各個節點的值打印出來
  • printf("\n");
  • printf("=========== print the list ===============\n");
  • list_for_each(pos, &person_head.list)
  • {
  • // 這里我們用list_entry來取得pos所在的結構的指針
  • tmp = list_entry(pos, struct person, list);
  • printf("age:%d, weight: %d \n", tmp->age, tmp->weight);
  • }
  • printf("\n");
  • // 下面刪除一個節點中,age為20的節點
  • printf("========== print list after delete a node which age is 20
  • 50.==========\n");

  • list_for_each_safe(pos, n, &person_head.list)
  • {
  • tmp = list_entry(pos, struct person, list);
  • if(tmp->age == 20)
  • {
  • list_del_init(pos);
  • free(tmp);
  • }
  • }
  • list_for_each(pos, &person_head.list)
  • {
  • tmp = list_entry(pos, struct person, list);
  • printf("age:%d, weight: %d \n", tmp->age, tmp->weight);
  • }
  • // 釋放資源
  • list_for_each_safe(pos, n, &person_head.list)
  • {
  • tmp = list_entry(pos, struct person, list);
  • list_del_init(pos);
  • free(tmp);
  • }
  • return 0;
  • 78.}

    轉載于:https://www.cnblogs.com/fastwave2004/p/4940166.html

    總結

    以上是生活随笔為你收集整理的[转]深入理解linux内核list_head的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

    主站蜘蛛池模板: 在线视频日韩精品 | 国产成人综合一区二区三区 | 色5566 | 国产又粗又黄又爽视频 | 91精品久久久久久粉嫩 | а中文在线天堂 | www四虎com | 西西大胆午夜视频 | 日本乱淫视频 | 亚洲视频免费播放 | 欧美jizz欧美性大全 | 夜晚福利| 九九精品九九 | 午夜理伦三级理论 | 国产xxxx18| 日韩在线播放中文字幕 | 亚洲成人免费观看 | 午夜免费福利影院 | 国产人妻人伦精品1国产 | 欧美永久| 亚洲精品久久视频 | 午夜噜噜 | 激情欧美网站 | caoprom超碰 | 台湾无码一区二区 | 朝桐光在线观看 | 金瓶狂野欧美性猛交xxxx | 国产视频精品在线 | www.x日本| 亚洲欧洲中文 | 久久久久99人妻一区二区三区 | 久草播放| 夜av| 国产视频xxxx | 91在线免费看 | 黄色一级带 | 亚洲国产精品免费视频 | 天天摸天天干 | 一本大道一区二区 | 成人福利在线视频 | 国产av无码专区亚洲精品 | 自拍偷拍亚洲区 | 日韩视频免费观看高清完整版在线观看 | 蜜桃精品久久久久久久免费影院 | 制服丝袜中文字幕在线 | 阿v天堂在线观看 | 美女伦理水蜜桃4 | 娇小的粉嫩xxx极品 国产精品人人爽人人爽 | 拍真实国产伦偷精品 | 综合精品久久 | 国产一区二区三区在线免费观看 | 日韩午夜小视频 | 911亚洲精选| 毛片aaa| 日韩淫视频| 国产极品福利 | 成人免费毛片网 | 国产精品美女www爽爽爽 | 国产不卡av在线播放 | 高清av免费观看 | 91久久人澡人人添人人爽欧美 | 先锋影音男人 | 国产精品丝袜一区 | 狠狠网站| 天天综合91 | 色综合天| 精品一区二区久久久久久久网站 | 日韩不卡av | 日韩视频网址 | 欧美三级免费观看 | 另类中文字幕 | 亚洲福利视频一区二区 | 成人激情片 | 欧美日韩精品在线观看 | eeuss鲁片一区二区三区在线观看 | 亚洲免费福利视频 | 日本黄色一区二区三区 | 欧美精品免费在线 | 成人av动漫 | 日韩有码在线播放 | 女同性恋毛片 | 国产福利在线播放 | 国产精品电影网 | 亚洲熟妇av日韩熟妇在线 | 亚洲国产精品99久久久久久久久 | 日韩毛片 | 在线日韩亚洲 | 国产高潮又爽又无遮挡又免费 | 午夜精产品一区二区在线观看的 | 色欲av无码精品一区 | 亚洲精品第三页 | 性xxxx欧美老肥妇牲乱 | 九九色播 | youjizzxxx69| 亚洲男人精品 | 爱欲av | 一区二区三区91 | 成人激情四射网 | 永久国产 |