在linux核心中,提供了乙個用來建立雙向迴圈鍊錶的結構 list_head。雖然linux核心是用c語言寫的,但是list_head的引入,使得核心資料結構也可以擁有物件導向的特性,通過使用操作list_head 的通用介面很容易實現**的重用,有點類似於c++的繼承機制(希望有機會寫篇文章研究一下c語言的物件導向機制)。下面就是kernel中的list_head結構定義:
struct list_head
需要注意的一點是,頭結點head是不使用的,這點需要注意。
使用list_head組織的鍊錶的結構如下圖所示:
list_head這個結構看起來怪怪的,它竟沒有資料域!所以看到這個結構的人第一反應就是我們怎麼訪問資料?
其實list_head不是拿來單獨用的,它一般被嵌到其它結構中,如:
struct file_node )
這裡涉及到三個巨集,還是有點複雜的,我們乙個乙個來看:
#define offsetof(type,member) ( (size_t)& ((type *)0)-> member )
我們知道 0 位址內容是不能訪問的,但 0位址的位址我們還是可以訪問的, 這裡用到乙個取址運算子
(type *)0 它表示將 0位址強制轉換為type型別,((type *)0)-> member 也就是從0址址找到type 的成員member 。
我們結合上面的結構來看
struct file_node )
這個巨集是由兩個語句組成,最後container_of返回的結果就是第二個表示式的值。這裡__mptr為中間變數,這就是list_head指標型別,它被初始化為ptr的值,而ptr就是當前所求的結構體中list_head節點的位址。為什麼要用中間變數,這是考慮到安全性因素,如果傳進來乙個ptr++,所有ptr++放在乙個表示式中會有***,像 (p++)+(p++)之類。
(char*)__mptr 之所以要強制型別轉化為char是因為位址是以位元組為單位的,而char的長度就是乙個位元組。
container_of的值是兩個位址相減,
剛說了__mptr是結構體中list_head節點的位址,offset巨集求的是list_head節點member在結構體type中的偏移量,那麼__mptr減去它所在結構體中的偏移量,就是結構體的位址。
所以list_entry(ptr,type,member)巨集的功能就是,由結構體成員位址求結構體位址。其中ptr 是所求結構體中list_head成員指標,type是所求結構體型別,member是結構體list_head成員名。通過下圖來總結一下:
繼續列舉一些雙鏈表的常用操作:
雙向鍊錶的遍歷——list_for_each
//注:這裡prefetch 是gcc的乙個優化選項,也可以不要
#define list_for_each(pos, head) \
for (pos = (head)->next; prefetch(pos->next), pos != (head); \
pos = pos->next)
生成雙向鍊錶的頭結點——list_head()
list_head() -- 生成乙個名為name的雙向煉表頭節點
#define list_head(name) \
struct list_head name = list_head_init(name)
static inline void init_list_head(struct list_head *list)
list->next = list;
list->prev = list;
雙向鍊錶的插入操作 --list_add()
將new所代表的結構體插入head所管理的雙向鍊錶的頭節點head之後: (即插入表頭)
static inline void list_add(struct list_head *new, struct list_head *head)
__list_add(new, head, head->next);
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;
從list中刪除結點——list_del()
static inline void list_del(struct list_head *entry)
__list_del(entry->prev, entry->next);
entry->next = list_poison1;
entry->prev = list_poison2;
static inline void __list_del(struct list_head * prev, struct list_head * next)
next->prev = prev;
prev->next = next;
判斷鍊錶是否為空(如果雙向鍊錶head為空則返回真,否則為假)——list_empty()
static inline int list_empty(const struct list_head *head)
return head->next == head;
Linux 核心list head 學習
在linux核心中,提供了乙個用來建立雙向迴圈鍊錶的結構 list head。雖然linux核心是用c語言寫的,但是list head的引入,使得核心資料結構也可以擁有物件導向的特性,通過使用操作list head 的通用介面很容易實現 的重用,有點類似於c 的繼承機制 希望有機會寫篇文章研究一下c...
Linux 核心list head 學習
在linux核心中,提供了乙個用來建立雙向迴圈鍊錶的結構 list head。雖然linux核心是用c語言寫的,但是list head的引入,使得核心資料結構也可以擁有物件導向的特性,通過使用操作list head 的通用介面很容易實現 的重用,有點類似於c 的繼承機制 希望有機會寫篇文章研究一下c...
Linux 核心list head 學習
linux 核心list head 學習 一 在linux 核心中,提供了乙個用來建立雙向迴圈鍊錶的結構 list head 雖然linux 核心是用 c語言寫的,但是 list head 的引入,使得核心資料結構也可以擁有物件導向的特性,通過使用操作 list head 的通用介面很容易實現 的重...