Linux核心 10 核心中的鍊錶

2021-09-07 16:52:35 字數 2834 閱讀 9952

使用鍊錶的目的很明確,因為有很多事情要做,於是就把它放進煉表裡,一件事一件事的處理。比如在usb子系統裡,u盤不停的提交urb請求,usb鍵盤也提交,usb滑鼠也提交,那usb主機控制器咋應付得過來呢?很簡單,建乙個鍊錶,然後你每次提交就是往裡邊插入,然後usb主機控制器再統一去排程,乙個乙個來執行。這裡有力得證明了,譚浩強大哥的c程式設計是我們學習linux的有力**,書中對鍊錶的介紹無疑是英明的,譚大哥,您不是乙個人在戰鬥!

核心中煉表的實現位於include/linux/list.**件,鍊錶資料結構的定義也很簡單。

21 struct list_head ;

list_head結構包含兩個指向list_head結構的指標prev和next,由此可見,核心中的鍊錶實際上都是雙鏈表(通常都是雙迴圈鍊錶)。

通常,我們在資料結構課堂上所了解的鍊錶定義方式是這樣的(以單鏈表為例):

struct list_node ;

通過這種方式使用鍊錶,對每一種資料型別,都要定義它們各自的鍊錶結構。而核心中的鍊錶卻與此不同,它並沒有資料域,不是在鍊錶結構中包含資料,而是在描述資料型別的結構中包含鍊錶。

比如在hub驅動中使用struct usb_hub來描述hub裝置,hub需要處理一系列的事件,比如當探測到乙個裝置連進來時,就會執行一些**去初始化該裝置,所以hub就建立了乙個鍊錶來處理各種事件,這個鍊錶的結構如下圖。

(1)宣告與初始化。

鍊錶的宣告可以使用兩種方式,一種為使用list_head巨集在編譯時靜態初始化,一種為使用init_list_head()在執行時進行初始化。

25 #define list_head_init(name)

26 27 #define list_head(name) /

28   struct list_head name = list_head_init(name)

2930 static inline void init_list_head(struct list_head *list)

31

無論採用哪種方式,新生成的煉表頭的兩個指標next、prev都初始化為指向自己。

(2)判斷鍊錶是否為空。

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

299

(3)插入。

有了鍊錶,自然就要往裡面加東西、減東西。就像我們每個人每天都在不停的走進去,又走出來,似是夢境又不是夢境。一切都是不經意的。走進去是一年四季,走出來是春夏秋冬。list_add()和list_add_tail()這兩個函式就是往佇列裡加東西。

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

68

84 static inline void list_add_tail(struct list_head *new, struct list_head *head)

85

其中,list_add()將資料插入在head之後,list_add_tail()將資料插入在head->prev之後。其實對於迴圈鍊錶來說,表頭的next、prev分別指向鍊錶中的第乙個和最後乙個節點,所以,list_add()和list_add_tail()的區別並不大。

(4)刪除。

搞懂譚浩強那本書之後看這些鍊錶的**那就是小菜一碟。再來看下乙個 list_del_init(),裡的元素不能只加不減,沒用了的元素就該刪除掉,把空間騰出來給別人。郭敬明說過,我生命裡的溫暖就那麼多,我全部給了你,但是你離開了我,你叫我以後怎麼再對別人笑……

煉表裡的元素不能只加不減,沒用了的元素就應該刪除掉。

254 static inline void list_del_init(struct list_head *entry)

255

list_del_init()從煉表裡刪除乙個元素,並且將其初始化。

(5)遍歷。

核心中的鍊錶僅僅儲存了list_head結構的位址,我們如何通過它或取乙個鍊錶節點真正的資料項?這就要提到有關鍊錶的所有操作裡面,最為重要超級經典的list_entry巨集了,我們可以通過它很容易地獲得乙個鍊錶節點的資料。

425 #define list_entry(ptr, type, member) /

426  container_of(ptr, type, member)

我相信,list_entry()這個巨集在linux核心**中的地位,就相當於廣告詞中的任靜付笛生的洗洗更健康,相當於大美女關之琳的一分鐘輕鬆做女人,這都是耳熟能詳婦孺皆知的,是經典中的經典。如果你說你不知道list_entry(),那你千萬別跟人說你懂linux核心,就好比你不知道陳文登不知道任汝芬你就根本不好意思跟人說你考過研,要知道每個考研人都是左手一本陳文登右手一本任汝芬。

可惜,關於list_entry,這個譚浩強老師的書裡就沒有了,當然你不能指責譚浩強的書不行,再好的書也不可能包羅永珍。

關於list_entry(),讓我們結合例項來看,還是hub驅動的那個例子,當我們真的要處理hub的事件的時候,我們當然需要知道具體是哪個hub觸發了這起事件。而list_entry的作用就是,從struct list_head event_list得到它所對應的struct usb_hub結構體變數。比如以下四行**:

struct list_head *tmp;

struct usb_hub *hub;

tmp = hub_event_list.next;

hub = list_entry(tmp, struct usb_hub, event_list);

從全域性鍊錶hub_event_list中取出乙個來,叫做tmp,然後通過tmp,獲得它所對應的struct usb_hub。

Linux核心中的鍊錶

要使用核心鍊錶,需要包含標頭檔案,鍊錶結構定義如下 struct list head linux核心中的鍊錶是乙個雙向迴圈鍊錶,結構中只包含兩個指標next和prev,不包含其它任何資料成員,同常見定義鍊錶稍有不同。在linux核心中使用鍊錶,是將鍊錶內嵌在其它結構體中來使用,從這裡也可以看出,li...

Linux核心中煉表處理

最基本的定義 struct list head define container of ptr,type,member 通過指標ptr,獲取對應的結構體指標。其中member是typeof中的指標。ptr就是指向member。define list for each entry pos,head,m...

Linux核心裝置驅動之核心中煉表的使用筆記整理

核心中煉表的應用 1 介紹 在linux核心中使用了大量的鍊錶結構來組織資料,包括裝置列表以及各種功能模組中的資料組織。這些鍊錶大多採用在include linux list.h實現的乙個相當精彩的鍊錶資料結構。鍊錶資料結構的定義很簡單 struct list head list head結構包含兩...