linux核心中list head使用介紹

2021-06-21 14:13:52 字數 2353 閱讀 5029

list_head結構的介紹

list_head結構定義在

struct list_head ;

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

#define list_head(name) \

struct list_head name =

#define init_list_head(ptr) do while (0)

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

list_head(hello)

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

static __inline__ int list_empty(struct list_head *head)

現在我們來介紹如何加入或刪除list_head到上面的hello序列裡。 linux提供二個函式來做這些事, 分別是list_add()和lis_del()。 這兩個函式的定義都放在

#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通常是去參考到不合法的記憶體位址內容所造成的, 如果我們加上了&就表示我們沒有要去參考這個不合法位址的內容,我們只是要那個欄位的位址而已, 因此, 不會造成segmentation fault。 其實, 結構的配置在記憶體裡是連續的。 所以, 如果我們去讀取某個欄位時,像&hello->list。 會先取得hello變數的位址, 再然後再計算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。 也就是這個欄位在helloworld結構裡的offset。

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

struct list_head *plist = &hello.list;

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

而這種方式就是list_head的用法, 它是專門用來當作別的結構的字段,只要我們得到這個欄位的位置和包含這個欄位的結構是那一種, 我們可以很輕易的算出包含此字段的結構位址, 圖6就是super block在使用list_head所得到的結果。只要我們知道s_list的位址, 只要呼叫

list_entry( &sb1.s_list, struct super_block, s_list)

就可以得到其sb1這個super_block結構的位址。

Linux核心中的list for each

在linux核心原始碼中,經常要對鍊錶進行操作,其中乙個很重要的巨集是list for each entry 意思大體如下 假設只有兩個結點,則第乙個member代表head,list for each entry的作用就是迴圈遍歷每乙個pos中的member子項。巨集list for each e...

linux核心中container of詳解

在linux 核心中,container of 函式使用非常廣,例如 linux核心鍊錶 list head 工作佇列work struct中。在linux 核心中有乙個大名鼎鼎的巨集container of 這個巨集是用來幹嘛的呢?我們先來看看它在核心中是怎樣定義的。呵呵,乍一看不知道是什麼東東。...

linux核心中ffs x 巨集

linux核心中ffs x 巨集是平台相關的巨集,在arm平台,該巨集定義在 arch arm include asm bitops.h define ffs x static inline int fls int x t t 等於找到 t 第乙個為1的位 從低位開始 並把該位保留為1其餘位清0.例...