Linux 內核中鏈表的定義和常見的操作

0x10 鏈表演化

鏈表是內核中最常見的數據結構,也是其他數據結構的基礎。和數組顯著的區別是,鏈表無需佔用連續的存儲單元,在編譯時,也不需要知道其長度,可以動態插入或者刪除元素。下圖是一個典型的雙向鏈表

圖1 雙向鏈表 1

通過前驅和後繼兩個指針,就可以遍歷整個鏈。如果打亂前驅和後繼的關係,就形成了二叉樹;如果設計更多的指針域,就可以構成各種複雜的數據結構;如果減少一個指針域,就形成單鏈表,如果讓首結點的前驅指向尾結點,則形成循環鏈表;如果只能對鏈表的頭進行插入和刪除操作,則是;如果只能對鏈表的首尾進行插入或刪除操作,則是隊列


0x20 鏈表的定義和操作

鏈表的定義

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

這是一個不含數據域的鏈表,可以嵌套到任何結構中,比如,可以按照如下方式定義一個真正含有數據域的鏈表

struct my_list{
	void * my_data;
	struct list_head list;
}

請注意:結構體的嵌套看似指向自己,其實是指向同一類型的不同結構。

鏈表的聲明和初始化宏

實際上,struct list_head 只是定義了鏈表結點,並沒有定義鏈表的表頭,內核代碼 list.h 中定義了兩個宏

  • define LIST_HEAD_INIT(name) { &(name), &(name) } /* 僅初始化*/
  • define LIST_HEAD(name) struct list_head_name = LIST_HEAD_INIT(name) /* 聲明並初始化*/

調用之後,mylist_head 的前驅和後繼指針都初始化指向自己,這就是一個空鏈表。判斷鏈表是否爲空,就是讓鏈表的頭指向自己。

鏈表增加一個結點

內核代碼 list.h 增加結點的函數是

  • static inline void list_add()
  • static inline void list_add_tail()

static 表明這是靜態函數,所謂的靜態函數,實際上是對函數作用域的限制,當前函數僅在文件內有效;inline,表明這是內聯函數,對編譯程序可見,編譯器在調用這個函數時,立刻展開該函數。故而,關鍵字 inline 必須與函數定義體放在一起,才能夠使得函數稱爲內聯函數。inline 函數一般放在頭文件中。

這兩個函數到底怎麼實現的呢?首先看一下如下的內部函數(兩個下劃線就表示內部函數)

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);
}

鏈表的循環

鏈表的循環是一個比較重要的概念,內核代碼 list.h 定義瞭如下遍歷鏈表的宏

  • define list_for_each(pos, head) for(pos = (head)->next; pos != (head); pos = pos->next)

這種方式,只是找到每個結點在鏈表中的偏移,有個較爲關鍵的問題是,怎麼通過pos找到結點的起始位置。
在這裏插入圖片描述
圖2 遍歷鏈表

list.h 還定義了 list_entry() 宏,即從一個結構的成員指針找到其容器的指針

#define list_entry(ptr, type, member) / 
((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))

(unsigned long)(&((type *)0 把地址0轉換成 type 結構的指針,緊接着獲取該結構中 member 域的指針,也就獲得了 member 在 type中的偏移量。(type *)(ptr) 得到 ptr 指針的絕對地址,相減,得到 type 類型結構體的起始地址。
在這裏插入圖片描述
圖3 list_entry() 宏

以上就是內核代碼中對鏈表的一些初始操作。


  1. 《Linux 操作系統原理與應用》第2版 ↩︎

發佈了26 篇原創文章 · 獲贊 22 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章