亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 開發 > Linux Shell > 正文

Linux內核鏈表實現過程

2020-07-27 19:21:52
字體:
來源:轉載
供稿:網友

關于雙鏈表實現,一般教科書上定義一個雙向鏈表節點的方法如下:

復制代碼 代碼如下:

struct list_node{
stuct list_node *pre;
stuct list_node *next;
ElemType data;
}

即一個鏈表節點包含:一個指向前向節點的指針、一個指向后續節點的指針,以及數據域共三部分。
但查看linux內核代碼中的list實現時,會發現其與教科書上的方法有很大的差別。
來看看linux是如何實現雙鏈表。
雙鏈表節點定義
復制代碼 代碼如下:

struct list_head {
 struct list_head *next, *prev;
};

發現鏈表節點中根本就沒有數據域,這樣的鏈表有什么用?linux內核中定義這樣的鏈表原因何在?
這是因為linux中是通過獨立定義一個鏈表結構,并在結構體中內嵌一個鏈表節點來實現鏈表結構的。這樣有一個好處就是能達到鏈表與結構體分離的目的。如此一來,我們構建好一個鏈表后,其結構示意圖如下:

鏈表的定義及初始化宏定義:
復制代碼 代碼如下:

#define LIST_HEAD_INIT(name){&(name),&(name)} 
#define LIST_HEAD(name) /
      struct list_head name = LIST_HEAD_INIT(name)
#define INIT_LIST_HEAD(ptr) do { /
      (ptr)->next = (ptr); (ptr)->prev = (ptr);/
      } while (0)

LIST_HEAD(name)宏用來定義一個鏈表頭,并使他的兩個指針都指向自己。我們可以在程序的變量聲明處,直接調用LIST_HEAD(name)宏,來定義并初始化一個名為name的鏈表。也可以先聲明一個鏈表,然后再使用INIT_LIST_HEAD來初始化這個鏈表。
也即:
復制代碼 代碼如下:

 LIST_HEAD(mylist);
 與
 struct list_head mylist;
 INIT_LIST_HEAD(&mylist);

 是等價的。

插入操作

復制代碼 代碼如下:

/*僅供內部調用
  * Insert a new entry between two known consecutive entries.
  * This is only for internal list manipulation where we know
  * the prev/next entries already!
  */
static inline void __list_add(struct list_head *new,
         struct list_head *prev,
         struct list_head *next)
{
 next->prev = new;
 new->next = next;
 new->prev = prev;
 prev->next = new;
}
 

復制代碼 代碼如下:

//在頭節點后面插入一個節點
static inline void list_add(struct list_head *new, struct list_head *head)
{
 __list_add(new, head, head->next);
}
//在尾節點后插入一個節點
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
 __list_add(new, head->prev, head);
}


刪除操作
復制代碼 代碼如下:

static inline void __list_del(struct list_head * prev, struct list_head * next)
{
 next->prev = prev;
 prev->next = next;
}
static inline void list_del(struct list_head *entry)
{
 __list_del(entry->prev, entry->next);
}

刪除鏈表節點的操作很簡單,是通過將要刪除的節點的前一個節點與后一個節點鏈接到一起。
鏈表節點替換操作
 
復制代碼 代碼如下:

static inline void list_replace(struct list_head *old,
    struct list_head *new)
{
 new->next = old->next;
 new->next->prev = new;
 new->prev = old->prev;
 new->prev->next = new;
}
 


鏈表遍歷操作(重點在這里)
首先來看一個如何根據鏈表節點地址得到其所在結構體的地址。
復制代碼 代碼如下:

#define list_entry(ptr, type, member) container_of(ptr, type, member)
//container_of宏的定義如下:
#define container_of(ptr, type, member)({/
        const typeof(((type *)0)->member ) *__mptr = (ptr);/
        (type *)( (char *)__mptr - offsetof(type,member) );})
//offsetof的宏定義如下:
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
將上述簡化一下成為下面這樣:
#define list_entry(ptr, type, member) /
  ((type *)((char *)(ptr)-(size_t)(&((type *)0)->member)))

是一個帶3個參數的宏,該宏的作用是獲取鏈表節點(ptr)所在結構體的起始地址。有了這個宏,我們只要知道某一個鏈表節點指針,就可以通過該鏈表節點得到其所在結構體的指針,從而,我們遍歷鏈表,也便可以達到遍歷我們自己定義的結構體。第一個參數為一個地址,他是結構體鏈表節點元素的地址,第二個參數是結構體類型,第三個參數是鏈表節點元素在結構體中的名字。
來仔細分析一下這個宏:
最外面的一層括號可以去掉,這是為了防止宏擴展的,去掉如下:
(type *) ((char *)(ptr)-(size_t)(&((type *)0)->member))
現在就比較清楚了,首先(type *)是C強制轉換操作,就是將后面的的數據轉化成type結構的指針。而后面的操作可以再分解
(char *)(ptr) - (size_t)(&((type *)0)->member)
 這樣就是一個減法的操作,前面是一個指針,我們傳過去的結構體鏈表節點元素的指針,這里被轉化成指向字符的。而后面是一個整形,可以再分解
(size_t) (&((type *)0)->member)
顯然這個整形是一個指針轉化的,而這個指針又可以再分解,
&((type *)0)->member
     可以看出這個指針是一個變量取地址得到的,這個變量又是什么呢
((type *)0)->member
     看起來有點奇怪,不過這個操作是整個宏中最精妙的,他將地址0轉化成type類型,接下來又取得這個結構的member元素,member就是我們傳進來的參數:元素在結構體中的命名。其實((type *)0)->member取的變量是內容是什么一點都不重要,重要的我們要取這個變量的地址。取完這個地址將它轉換成size_t類型,這樣這個數據就是((type *)0)->member相對與地址0的偏移。回到上面的那個減法,將結構體中鏈表節點元素的地址與他與結構體首地址的偏移相減,不就得到了結構體的地址了嗎。)(&((type *)0)->member)))
    最外面的一層括號可以去掉,這是為了防止宏擴展的,去掉如下:
(type *) ((char *)(ptr)-(size_t)(&((type *)0)->member))
     現在就比較清楚了,首先(type *)是C強制轉換操作,就是將后面的數據轉化成type結構的指針。而后面的操作可以再分解
(char *)(ptr) - (size_t)(&((type *)0)->member)
     這樣就是一個減法的操作,前面是一個指針,我們傳過去的結構體元素的指針,這里被轉化成指向字符的。而后面是一個長整形,可以再分解
(size_t) (&((type *)0)->member)
     顯然這個長整形是一個指針轉化的,而這個指針又可以再分解,
&((type *)0)->member
     可以看出這個指針是一個變量取地址得到的,這個變量又是什么呢?
((type *)0)->member
     起來有點奇怪,不過這個操作是整個宏中最精妙的,他將地址0轉化成type類型,接下來又取得這個結構的member元素,member就是我們傳進來的參數:元素在結構體中的命名。其實((type *)0)->member取的變量是內容是什么一點都不重要,重要的我們要取這個變量的地址。取完這個地址將它轉換成size_t類型,這樣這個數據就是((type *)0)->member相對與地址0的偏移?;氐缴厦娴哪莻€減法,將結構體中元素的地址與他與結構體首地址的偏移相減,便得到了結構體的地址了。
鏈表的遍歷操作時通過一個宏來實現的:
復制代碼 代碼如下:

#define list_for_each(pos, head) /
   for(pos = (head)->next, prefetch(pos->next);pos!=(head);/
        pos = pos->next,prefetch(pos->next))

其中prefetch是用于性能優化,暫時不用去管它。
從上述鏈表遍歷宏可以看出,其只是一次獲得了鏈表節點指針,在實際應用中,我們都需要獲取鏈表節點所在結構體的數據項,因此,通常將list_for_each和list_entry一起使用。為此,linux的list實現提供了另外一個接口如下:
復制代碼 代碼如下:

#define list_for_each_entry(pos, head, member)/
 for(pos = list_entry((head)->next, typeof(*pos), member);/
    prefetch(pos->member.next), &pos->member != (head);/
    pos = list_entry(pos->member.next, typeof(*pos), member))
 

有了這個接口,我們就可以通過鏈表結構來遍歷我們實際的結構體數據域了。
例如,我們定義了一個結構體如下:
復制代碼 代碼如下:

struct mystruct{
ElemType1 data1;
ElemType2 data2;
strcut list_head anchor;//通常我們稱結構體內的鏈表節點為鏈表錨,因為它有定位的作用。
}

那么我們遍歷鏈表的代碼如下:
復制代碼 代碼如下:

struct mystruct  *pos;
list_for_each_entry(pos,head,anchor){
mystruct *pStruct=pos;
//do something with pStruct.....
}

此外Linux鏈表還提供了兩個對應于基本遍歷操作的"_safe"接口:list_for_each_safe(pos, n, head)、list_for_each_entry_safe(pos, n, head, member),它們要求調用者另外提供一個與pos同類型的指針n,在for循環中暫存pos下一個節點的地址,避免因pos節點被釋放而造成的斷鏈。
當然,linux鏈表不止提供上述接口,還有
復制代碼 代碼如下:

list_for_each_prev(pos, head)
list_for_each_prev_safe(pos, n, head)
list_for_each_entry_reverse(pos, head, member)
list_prepare_entry(pos, head, member)
static inline int list_empty_careful(const struct list_head *head)
static inline void list_del_init(struct list_head *entry)
static inline void list_move(struct list_head *list, struct list_head *head)
static inline void list_move_tail(struct list_head *list,
struct list_head *head)
static inline int list_empty(const struct list_head *head)

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
亚洲成**性毛茸茸| 欧美性xxxx| 欧美色道久久88综合亚洲精品| 亚洲欧洲国产精品| 精品久久久久久中文字幕一区奶水| 亚洲最大福利视频| 成人在线观看视频网站| 日韩在线视频线视频免费网站| 亚洲香蕉伊综合在人在线视看| 久久九九全国免费精品观看| 日韩电影视频免费| 精品欧美一区二区三区| 久久久在线免费观看| 亚洲性xxxx| 亚洲黄页网在线观看| 成人黄色影片在线| 色婷婷av一区二区三区在线观看| 久久久国产一区| 久久99国产综合精品女同| 亚洲欧洲一区二区三区在线观看| 在线观看视频亚洲| 91av在线免费观看| 国产在线精品一区免费香蕉| 日韩美女福利视频| 久久久久久高潮国产精品视| 亚洲精品一二区| 日韩成人激情视频| 欧美性猛交xxxx乱大交极品| 色婷婷av一区二区三区久久| 久久不射电影网| 91久久精品日日躁夜夜躁国产| 日韩中文字幕国产精品| 亚洲人成电影网站色…| 日韩欧美中文在线| 久久久噜噜噜久久中文字免| 亚洲天堂av电影| 久久久久日韩精品久久久男男| 欧美大片在线影院| 国产精品草莓在线免费观看| 中日韩美女免费视频网站在线观看| 伊人成人开心激情综合网| 色偷偷亚洲男人天堂| 日韩不卡中文字幕| 亚洲 日韩 国产第一| 97成人精品区在线播放| 成人网中文字幕| 欧美日韩免费观看中文| 日韩精品免费综合视频在线播放| 97视频在线播放| 国产一区二区精品丝袜| 国外成人性视频| 成人xvideos免费视频| 亚洲国产精品久久91精品| 日本中文字幕久久看| 国产精品久久77777| 欧美人成在线视频| 日韩大陆毛片av| 亚洲欧美精品在线| 国产97人人超碰caoprom| 91wwwcom在线观看| 久久精品男人天堂| 欧美性猛交xxxx久久久| 91香蕉嫩草神马影院在线观看| 91精品国产色综合| 日韩美女免费视频| 欧美精品在线极品| 国产xxx69麻豆国语对白| 欧美高清激情视频| 日韩网站免费观看| 亚洲国产成人精品一区二区| 日韩精品小视频| 狠狠爱在线视频一区| 欧美日韩激情视频| 欧美日韩高清在线观看| 久久久久久久久爱| 亚洲天堂男人天堂| 国产97色在线| 国产精品主播视频| 精品亚洲va在线va天堂资源站| 久久福利视频网| 日韩精品久久久久久福利| 中文字幕日韩电影| 日韩av成人在线| 国产美女精品视频| 欧美激情精品久久久久久蜜臀| 久久精品视频亚洲| 91精品国产色综合久久不卡98| 国产综合视频在线观看| 国产精品亚洲美女av网站| 亚洲第一在线视频| 日韩中文字幕国产精品| 91欧美精品午夜性色福利在线| 国产91精品久久久久久久| 欧美黑人xxxⅹ高潮交| 欧美日韩国内自拍| 91成人在线播放| 日韩在线观看免费高清完整版| 成人黄色免费网站在线观看| 91免费版网站入口| 国产日韩中文字幕| 久久国产精品99国产精| 国产精品视频一区国模私拍| 成人a免费视频| 中文字幕一区日韩电影| 亚洲欧美日韩一区二区三区在线| 欧美高清在线观看| 深夜福利日韩在线看| 久久久久久久国产精品视频| 日韩免费av一区二区| 97国产精品视频人人做人人爱| 日韩欧美大尺度| 庆余年2免费日韩剧观看大牛| 国产精品久久久久免费a∨大胸| 亚洲精品理论电影| 国产亚洲精品久久| 日韩精品在线视频| 国产剧情日韩欧美| 日韩中文字幕在线看| 国产精品av在线播放| 国产小视频国产精品| 国产97在线播放| 深夜精品寂寞黄网站在线观看| 在线观看91久久久久久| 亚洲性生活视频在线观看| 亚洲第一网站免费视频| 91免费在线视频网站| 欧美电影在线播放| 欧美大片在线影院| 国产精品7m视频| 国产亚洲视频中文字幕视频| 美女黄色丝袜一区| 日本sm极度另类视频| 91国内免费在线视频| 国产成+人+综合+亚洲欧美丁香花| 亚洲成人1234| 亚洲男人天堂手机在线| 国产日韩av高清| 日韩精品一区二区三区第95| 色悠悠久久88| 日韩欧美国产一区二区| 中文字幕日韩欧美| 欧美精品少妇videofree| 久久中文字幕在线视频| 国产亚洲精品91在线| 国产91在线播放| 久久影视电视剧凤归四时歌| 97免费中文视频在线观看| 欧美日韩免费一区| 亚洲视频在线免费看| 欧美日韩另类在线| 亚洲欧美日韩国产精品| 亚洲欧洲偷拍精品| 91成人免费观看网站| 欧美性生交xxxxxdddd| 日韩精品免费在线观看| 国产精品美女免费视频| 日韩美女免费观看| 欧美一区深夜视频| 45www国产精品网站| 欧美专区日韩视频| 亚洲成人av在线| 欧美中文字幕在线播放| 黄色精品一区二区| 欧美成人精品在线|