linux核心資料基本結構1 核心鍊錶

2021-10-01 17:25:13 字數 2724 閱讀 3001

linux核心**大量使用了鍊錶這種資料結構。鍊錶是在解決陣列不能動態擴充套件這個缺陷而產生的一種資料結構。鍊錶所包含的元素可以動態建立並插入和刪除。鍊錶的每個元素都是離散存放的,因此不需要占用連續的記憶體。鍊錶通常由若干節點組成,每個節點的結構都是一樣的,由有效資料區和指標區兩部分組成。有效資料區用來儲存有效資料資訊,而指標區用來指向鍊錶的前繼節點或者後繼節點。因此,鍊錶就是利用指標將各個節點串聯起來的一種儲存結構;

linux中的鍊錶巧妙的解決了這個問題,linux的鍊錶不是將使用者資料儲存在鍊錶節點中,而是將鍊錶節點儲存在使用者資料中

鍊錶和靜態陣列的區別:

1. 鍊錶包含的元素都是動態建立並插入鍊錶的,在編譯時不知道需要建立多少個元素。

2. 因為鍊錶中每個節點的建立時間各不相同,所以各節點在記憶體中無需占用連續記憶體區

(1)單向鍊錶

單向鍊錶的指標區只包含乙個指向下乙個節點的指標,因此會形成乙個單一方向的鍊錶,如下**所示。

struct list ;

如圖所示,單向鍊錶具有單向移動性,也就是只能訪問當前的節點的後繼節點,而無法訪問當前節點的前繼節點,因此在實際專案中運用得比較少。

(2)雙向鍊錶

如圖所示,雙向鍊錶和單向鍊錶的區別是指標區包含了兩個指標,乙個指向前繼節點,另乙個指向後繼節點,如下**所示。

(3)linux核心鍊錶實現

單向鍊錶和雙向鍊錶在實際使用中有一些侷限性,如資料區必須是固定資料,而實際需求是多種多樣的。這種方法無法構建一套通用的鍊錶,因為每個不同的資料區需要一套鍊錶。為此,linux核心把所有鍊錶操作方法的共同部分提取出來,把不同的部分留給**程式設計者自己去處理。linux核心實現了一套純煉表的封裝,鍊錶節點資料結構只有指標區而沒有資料區,另外還封裝了各種操作函式,如建立節點函式、插入節點函式、刪除節點函式、遍歷節點函式等。

可以看到通過list_head結構就把我們的資料層跟驅動層分開了,而核心提供的各種操作方法介面也只關心list_head這個結構,也就是具體鏈結的時候也只鏈結這個list_head結構,並不關心你資料層定義了什麼型別。

linux核心鍊錶使用struct list_head資料結構來描述。

struct list_head ;

煉表頭的初始化有兩種方法,一種是靜態初始化,另一種動態初始化。把next和prev指標都初始化並指向自己,這樣便初始化了乙個帶頭節點的空鍊錶。

/*靜態初始化*/

#define list_head_init(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()是把乙個節點新增到表頭,list_add_tail()是插入表尾。

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

list_add_tail(struct list_head *new, struct list_head *head)

遍歷節點的介面函式。

#define list_for_each(pos, head) \

for (pos = (head)->next; pos != (head); pos = pos->next)

那麼如何獲取節點本身的資料結構呢?這裡還需要使用list_entry()巨集

#define list_entry(ptr, type, member) \

container_of(ptr, type, member)

container_of()巨集的定義在kernel.h標頭檔案中。

補充:1:container_of

// 步驟1:將數字0強制轉型為type*,然後取得其中的member元素((type *)0)->member  // 相當於((struct student *)0)->list

// 步驟2:定義乙個臨時變數__mptr,並將其也指向ptr所指向的鍊錶節點const typeof(((type *)0)->member)*__mptr = (ptr);

// 步驟3:計算member字段距離type中第乙個欄位的距離,也就是type位址和member位址之間的差// offset(type, member)也是乙個巨集,定義如下:#define offsetof(type, member) ((size_t) &((type *)0)->member)

// 步驟4:將__mptr的位址 - type位址和member位址之間的差// 其實也就是獲取type的位址

Linux核心資料結構

一 概述 linux核心提供了一些常用的資料結構並建議開發者盡量重用,而不需要再去實現一些類似的資料結構。這篇部落格主要描述一下linux核心提供的鍊錶 佇列 對映及二叉樹這四種常用資料結構。當然這裡提到的每一種資料結構要說清楚都需要不少的篇幅,而這篇博文只是簡要分析一下linux核心設計的理念,以...

核心資料結構

關於開發驅動重要的核心資料結構,方便自己理解 driver object typedef struct driver object cshort type cshort size 乙個鍊錶,記錄了該驅動建立的所有裝置物件 pdevice object deiceobject ulong flags ...

核心資料結構

核心需要儲存i o元件使用的狀態資訊,可以通過若干核心資料結構比如說檔案開啟表等來完成 unix系統中在讀取乙個使用者檔案的時候,核心需要去檢查下快取,然後再去決定是否執行磁碟i o,在讀乙個程序映象時候,核心只需要從記憶體當中讀取資料,也就是說這些操作都可以呼叫read 函式來完成,但是語義不同 ...