Linux 核心list head 學習

2022-03-12 15:40:58 字數 4309 閱讀 5640

linux

核心list_head

學習(一)

在linux

核心中,提供了乙個用來建立雙向迴圈鍊錶的結構

list_head

。雖然linux

核心是用

c語言寫的,但是

list_head

的引入,使得核心資料結構也可以擁有物件導向的特性,通過使用操作

list_head

的通用介面很容易實現**的重用,有點類似於

c++的繼承機制(希望有機會寫篇文章研究一下

c語言的物件導向機制)。下面就是

kernel

中的list_head

結構定義:

struct list_head ;

#define list_head_init(name)

需要注意的一點是,頭結點

head

是不使用的,這點需要注意。

使用list_head

組織的鍊錶的結構如下圖所示:

這個結構看起來怪怪的,它竟沒有資料域!所以看到這個結構的人第一反應就是我們怎麼訪問資料?

其實list_head

不是拿來單獨用的,它一般被嵌到其它結構中,如:

struct file_node;

此時list_head

就作為它的父結構中的乙個成員了,當我們知道

list_head

的位址(指標)時,我們可以通過

list.c

提供的巨集

list_entry

來獲得它的父結構的位址。下面我們來看看

list_entry

的實現:

#define list_entry(ptr,type,member)\

container_of(ptr,type,member)

#define offsetof(type,member) ((size_t)&((type *)0)->member)

#define container_of(ptr,type,member) ( )

這裡涉及到三個巨集,還是有點複雜的,我們乙個乙個來看:

#define offsetof(type,member) ( (size_t)& ((type *)0

)-> member )

我們知道

0 位址內容是不能訪問的,但

0位址的位址我們還是可以訪問的,

這裡用到乙個取址運算子

(type *)0

它表示將

0位址強制轉換為

type

型別,((type *)0

)-> member

也就是從

0址址找到

type

的成員member

。我們結合上面的結構來看

struct file_node;

將實參代入

offset( struct file_node, node )

;最終將變成這樣:

( (size_t) & ((struct file_node*)0

)-> node )

;這樣看的還是不很清楚,我們再變變:

struct file_node *p = null;

& p->node;

這樣應該比較清楚了,即求

p 的成員

node

的位址,只不過p 為

0位址,從

0位址開始算成員

node

的位址,也就是

成員node

在結構體

struct file_node

中的偏移量。

offset

巨集就是算

member

在type

中的偏移量的。

我們再看第二個巨集

#define container_of(ptr,type,member) ( )

這個巨集是由兩個語句組成,最後

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_add()

將new

所代表的結構體插入

head

所管理的雙向鍊錶的頭節點

head

之後:

(即插入表頭)

static inline void list_add(struct list_head *new, struct list_head *head)

static inline void __list_add( struct list_head *new, struct list_head *prev, struct list_head *next) 從

list

中刪除結點

——list_del()

static inline void list_del(struct list_head *entry)

static inline void __list_del(struct list_head * prev, struct list_head * next)

判斷鍊錶是否為空(如果雙向鍊錶

head

為空則返回真,否則為假)

——list_empty()

static inline int list_empty(const struct list_head *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學習(二)

前一篇文章討論了list head 結構的基本結構和實現原理,本文主要介紹一下例項 自己如果想在應用程式中使用list head 的相應操作 當然應該沒人使用了,c stl提供了list 用起來貌似更方便 在應用程式中需要包含自己的 list.h 標頭檔案 注 這個list.h 是為了配合示例程式而...