Linux核心的List h分析

2021-09-30 11:10:02 字數 3340 閱讀 1213

1)struct list_head結構體

struct list_head ;

list_head 結構包含兩個指向list_head的結構體指標prev、next,所以該鍊錶具有雙向鍊錶的功能,但該鍊錶沒有資料區,該定義只給出了雙向鍊錶的結構,在實際使用中採用如下定義

struct stu ;

2 )鍊錶的初始化

#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_head_init(name)將name的位址直接分別賦值給next和prev,那麼它們事實上都指向自己,也形成乙個空鍊錶,上面的巨集提供了定義並初始化乙個list_head型別的結構體的功能  ,static修飾的函式就是靜態函式,是對函式作用域的限制,指該函式的作用域僅限於本檔案,不能被其他檔案使用,因此static具有資訊隱藏作用,inline修飾的函式的意思是這個函式對編譯程式是可見的,編譯程式在呼叫這個函式時就立即展開該函式,而不是儲存現場,執行呼叫函式,恢復現場等繁瑣操作,這樣可以提高效率

3 )鍊錶的插入

static inline void __list_add(struct list_head *new,struct list_head *prev,

struct list_head *next)

該函式指出插入的前一節點prev和後一節點next,讓後一節點的prev指向新節點,新的節點的next指向後一節點,讓新一節點的prev指向前一節點,讓前一節點prev的next指向新節點,從而插入該新節點

通過該函式還實現了以下兩個函式分別為頭插和尾插

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)

是通過呼叫__list_add函式引數的不同實現的,因為該list_head結構體為雙鏈表,其中實現**分別為:頭插__list_add(new, head, head->next);

尾插__list_add(new, head->prev, head);

4 )鍊錶的刪除

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

刪除是通過更改,前一節點的next域和後一節點的prev域,從而刪除節點,不需需要知道該元素,但必須知道該元素的前僕和後繼。操作後,此元素的前仆後繼成為彼此的前仆後繼,將指定元素擠出隊伍但並沒有消失,還可以訪問

static inline void list_del(struct list_head *entry)

此操作刪除乙個鍊錶中的指定元素。呼叫該函式後,指定元素從鍊錶中消失,它的前仆後繼成為彼此新的前仆後繼。其中list_poison1、list_poison2是因為刪除的元素其next和prev變數仍然是指向它以前的前仆後繼,也就是說將會導致從乙個非煉表中的物件可以訪問鍊錶物件,會引起嚴重的安全問題

list_poison1和list_poison定義在include/linux/poison.h中:

#define list_poison1 ((void *) 0x00100100 + poison_pointer_delta)

#define list_poison2 ((void *) 0x00200200 + poison_pointer_delta)

將該被拋棄的元素的內部指標指向兩個核心空間不會用到的記憶體位置,當試圖訪問該指標時會出現錯誤

5) 鍊錶的遍歷

list_for_each()  

#define list_for_each(pos, head) \

for (pos = (head)->next; prefetch(pos->next), pos != (head); \

pos = pos->next)

它實際上是乙個for迴圈,利用傳入的pos作為迴圈變數,從表頭head開始,逐項向後(next方向)移動pos,直至又回到head,停止迴圈,該巨集使用兩個list_head型別的引數,第乙個引數用來指向當前項,第二個引數指向需要檢索的鍊錶

當然使用該遍歷鍊錶只能得指向list_head結構體的指標,但是無法訪問其成員變數,為此linux提供了

#define list_entry(ptr, type, member) \

container_of(ptr, type, member)

假設設有乙個list_head的鍊錶,且每個鍊錶包含在另外乙個結構體的物件裡,這些外部結構體的物件通過list_head聯絡起來。如果ptr指向鍊錶中乙個已知的list_head物件,那麼list_entry巨集會返回包含這個物件的外部結構體物件的指標,從而實現訪問其成員變數的遍歷

6) 鍊錶的替換

static inline void list_replace(struct list_head *old,struct list_head *new)

乙個新的指定list_head物件搶走了舊的指定list_head物件的前仆後繼,表現就是該操作並沒有對舊的list_head物件做後續處理,也就是說它仍然指向它以前的前仆後繼,但原來的前仆後繼已經不指向它。

static inline void list_replace_init(struct list_head *old,

struct list_head *new)

此操作在list_replace()的基礎上,讓被替換出去的old元素重新初始化,使其不會讓它指向原有的鍊錶,讓其呼叫init_list_head函式指向它自己,保證鍊錶的安全性

7) 鍊錶的移動

static inline void list_move(struct list_head *list, struct list_head *head)

該函式在把乙個list_head物件list從他原來所在的鍊錶刪除後,通過list_add函式把他放到另外乙個鍊錶指定元素head的後面,即成為head的後繼,成為head原來後繼的前僕

static inline void list_move_tail(struct list_head *list,

struct list_head *head)

該函式在把乙個list_head物件list從他原來所在的鍊錶刪除後,通過list_add函式把他放到另外乙個鍊錶指定元素head的前面,即成為head的前僕,成為head原來前仆的後繼

Linux核心分析

從根本上看,核心是為了管理好程序來設計的,需要建立各種結構體來描述程序管理過程中需要用到的一切。首先,為什麼要劃分核心空間與使用者空間,出於安全的考慮,需要把使用者程式與核心隔離,這就帶來各種複雜性的東西,當然這是值得的。對於模組與模組的互動,使用者通過系統呼叫進入核心,這裡又包含中斷管理,異常管理...

linux核心分析四

使用庫函式api和c 中嵌入彙編 兩種方式使用同乙個系統呼叫,理解系統呼叫的工作機制。api 第一層是指libc中定義的api,這些api封裝了系統呼叫,使用int 0x80觸發乙個系統呼叫中斷 當然,並非所有的api都使用了系統呼叫,如完成數學加減運算的api就沒有使用系統呼叫 也有可能某個api...

Linux核心Crash分析

每乙個程序的生命週期內,其生命週期的範圍為幾毫秒到幾個月。一般都是和核心有互動,例如使用者空間程式使用系統呼叫進入核心空間。這時使用的不再是使用者空間的棧空間,使用對應的核心棧空間。對每乙個程序來說,linux核心都會把兩個不同的資料結構緊湊的存放在乙個單獨為程序分配的儲存空間中 乙個是核心態的程序...