list head使用介紹

2021-06-16 18:52:22 字數 4585 閱讀 8095

list_head結構的介紹

在list_head結構定義裡,它是乙個double linked list的結構。 如下::

kernel/include/linux/types.h:

struct list_head ;

有的人可能看到這樣的結構會覺得很奇怪這樣的結構可以存放資料嗎? 當然是不行的,因為這個結構根本是拿來讓人當資料存的。 首先, 我們先來看看兩個macro,

#define list_head(name) /

struct list_head name =

*****====>

#define list_head(name) \

struct list_head name = list_head_init(name)

} #define init_list_head(ptr) do while (0)

這兩個macro在kernel裡也算蠻常出現的, 是用來將list_head做初始化的,它的初始化就是將next和prev這兩個field設為跟結構的位址相同。 所以, 如果我們在程式裡看到這樣的**, 它的意思就是定義乙個list_head結構的變數hello,並將prev和next都設成hello的位址。

list_head(hello)

因此, 如果要檢查這個list是否是空的, 只要檢查hello.next是否等於&hello就可以了。事實上, linux也提供了乙個叫list_empty()的函式來檢查list是否為空的。

kernel/include/linux/list.h

/**

* list_empty - tests whether a list is empty

* @head: the list to test.

*/static inline int list_empty(const struct list_head *head)

現在我們來介紹如何加入或刪除list_head到上面的hello序列裡。 linux提供了2個函式來做這些事, 分別是list_add()和lis_del()。 這兩個函式的定義都放在 裡, 而且其程式**也都很簡單,只是單純double linked list的串接和刪除而已。

kernel/include/linux/list.h

/**

* 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.

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

static inline void list_add_tail(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_add 將這個新加入的new放在了list的head之後的第乙個元素,head->next=new;

而list_add_tail將新加入的new放在了list的尾部,讓head->prev=new

static inline void list_del(struct list_head *entry)

/* * delete a list entry by ****** the prev/next entries

* point to each other.

* * this is only for internal list manipulation where we know

* the prev/next entries already!

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

list_del將list中指定的enry刪除,同時將entry的prev 和entry-->next link在一起

關於list_head, 其實最重要的應該是它提供的這個macro。

#define list_entry(ptr, type, member) /

((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))

我們現在來做個實驗, 相信各位會更容易了解這個macro的。 請看一下下面這段程式碼。

struct helloworld  hello;

假設int是4個byte。 那麼以下這一行會得到8, 如圖5所示

(unsigned long) (&((struct helloworld *)0)->list)

有的人會對這一行程式感到奇怪, (struct helloworld*)0不就是乙個null的指標嗎? 怎麼可以用0->list去參考list這個欄位呢? 難道不怕造成segmentation fault嗎? 請注意一下, 我們在0->list的前面還加上了乙個&。 如果沒有&, 那上面這一行就會segmentation fault了。 如果你加上了&, 那就沒問題了。 segmentation fault通常是去參考不合法的記憶體位址內容所造成的, 如果我們加上了&就表示我們沒有要去參考這個不合法位址的內容,我們只是要那個field的位址而已, 因此, 不會造成segmentation fault。 其實, 結構的配置在記憶體裡是連續的。 所以, 如果我們去讀取某個filed時,像&hello->list。 會先取得hello變l量的位址, 再然後再計算helloworld結構裡list所在的offset, 再將hello的位址加上list的offset,求得list真正的位址。 然後再去讀list的內容。 這是compiler幫我們做的。 那我們現在就來看看上面那一行究竟是什麼意思。 首先, 我們先把上面那一行想象成下面這個樣子。

ptr = 0;

(unsigned long) (&((struct helloworld *)ptr)->list)

這樣是不是容易懂了嗎, 就是要取得&ptr->list的位址而已。所以, 如果ptr是100的話, 那會得到100+8=108。 因為前面有二個int, 每乙個int是4個byte。 經過轉型, 就得到了(unsigned long)型的108。 如果ptr是0的話, 那同理, 我們會得到0+8=8。 也就是這個filed在helloworld結構裡的offset。

現在, 如果我們已經知道了list在helloworld結構中的offset,而且我們現在也知道hello這個變數裡list的位址的話, 那有沒有辦法得到hello本身的位址呢? 可以的, 就 如果我們知道list的位址, 只要將list的位址減8就可以知道了hello的位址了嘛。

struct list_head *plist = &hello.list;

printf( "&hello = %x\n", (char*)plist - (unsigned long) 8 ));

而這種方式就是list_head的用法, 它是專門用來當作別的結構的filed,只要我們得到這個list_head的在結構中的偏移和該list_head的指標, 我們可以很輕易的算出包含此list_head的結構位址.

/**

* list_first_entry - get the first element from a list

* @ptr: the list head to take the element from.

* @type: the type of the struct this is embedded in.

* @member: the name of the list_struct within the struct.

* * note, that list is expected to be not empty.

*/#define list_first_entry(ptr, type, member) \

list_entry((ptr)->next, type, member)

list_first_entry 找到第乙個entry所對應的結構,type是該結構體所對應的型別

結合部落格 和自己所看的內容

linux核心中list head使用介紹

list head結構的介紹 list head結構定義在 struct list head 有的人可能看到這樣的結構會覺得很奇怪這樣的結構可以存放資料嗎?當然是不行的棉,因為這個結構根本是拿來讓人當資料存的。首先,我們先來看看兩個macro,define list head name struct...

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...