1. 程式人生 > 其它 >u-boot linux連結串列的管理

u-boot linux連結串列的管理

參考:https://blog.csdn.net/qqliyunpeng/article/details/53789082

感謝作者: 李雲鵬([email protected])

1. 連結串列資料結構list_head的定義:

Simple doubly linked list implementation. struct list_head { struct list_head *next, *prev; };

2. 宣告和初始化:有兩種方法

①宣告的時候初始化一個連結串列LIST_HEAD 巨集:

②執行時初始化連結串列INIT_LIST_HEAD 函式:

3. 插入/刪除/合併

a) 插入

對連結串列的插入操作有兩種:

在表頭插入list_add函式和在表尾插入list_add_tail函式:

b) 刪除

對連結串列的刪除操作函式有兩種:

list_del函式和

list_del_init函式:

c) 替換

對連結串列的替換操作有兩個:

list_replace函式和

list_replace_init函式:

d) 搬移

搬移的含義是將原本屬於一個連結串列的節點移動到另一個連結串列的操作,有兩個函式:

list_move函式和

list_move_tail函式:

e) 合併

合併在這裡的意思就是合併了,是將兩個獨立的連結串列合併成為一個連結串列,合併的時候根據合併的位置的不同可以分為:

合併到頭部的list_splice函式和

合併到尾部的list_splice_tail函式:(這兩個函式有推薦使用的函式)

4. 找到連結串列中的資料

前邊提到的函式都是操作的連結串列節點的入口,但是對於我們真正有意義的是節點上的資料,連結串列的頭上沒有資料,其他的節點上都是帶有資料的。如何從一個連結串列節點的入口得到節點的資料呢?要用到以下的函式:

list_entry函式:

/**
* list_entry - 獲得含連結串列入口的結構體首地址
* @ptr: member的首地址
* @type: 容器的型別
* @member: 要得到他的容器的某個成員
*/
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)

#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) // 將資料結構體放到0地址處,天然的結構體中成員的首地址就是成員在結構體中的偏移量

我們知道list_for_each_entry會用到list_entry,而list_entry用到container_of,而container_of中有offsetof,所以從offsetof講起。
offsetof(TYPE, MEMBER) ((size_t)&((TYPE *)0)->MEMBER)
理解offsetof的關鍵在於&((TYPE *)0)->MEMBER,幾乎可以說只要理解了這一部分,後面的幾個函式都能夠解決,那麼我們看看這一部分究竟完成了怎樣的工作。根據優先順序的順序,最裡面的小括號優先順序最高,TYPE *將整型常量0強制轉換為TYPE型的指標,且這個指標指向的地址為0,也就是將地址0開始的一塊儲存空間對映為TYPE型的物件,接下來再對結構體中MEMBER成員進行取址,而整個TYPE結構體的首地址是0, 這裡獲得的地址就是MEMBER成員在TYPE中的相對偏移量 。再將這個偏移量強制轉換成size_t型資料(無符號整型)。

首先可以看出container_of被預定義成一個函式,函式的第一句話,通過((type *)0)->member定義一個MEMBER型的指標__mptr,這個指標指向ptr,所以第一句話獲取到了我們要求的結構體,它的成員member的地址,接下來我們用這個地址減去成員member在結構體中的相對偏移量,就可以獲取到所求結構體的地址, (char *)__mptr - offsetof(type,member)就實現了這個過程,最後再把這個地址強制轉換成type型指標, 就獲取到了所求結構體指標, define預定義返回最後一句話的值, 將所求結構體指標返回。

所以整個container_of的功能就是通過指向結構體成員member的指標ptr獲取指向整個結構體的指標。container_of清楚了,那list_entry就更是一目瞭然了。

5. 遍歷連結串列

對linux核心的遍歷可以分為遍歷連結串列和遍歷連結串列中的結構體:

從頭開始遍歷連結串列,list_for_each巨集,

從頭開始遍歷連結串列中的結構體,list_for_each_entry巨集:

6. 安全性

只講一點判斷連結串列是不是為空:

list_empty巨集:

static inline int list_empty(const struct list_head *head)
{
  return head->next == head;
}