歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux綜合 >> Linux內核 >> Linux內核中的通用雙向循環鏈表

Linux內核中的通用雙向循環鏈表

日期:2017/3/1 9:30:58   编辑:Linux內核

開發中接觸Linux越來越多,休息放松之余,免不了翻看翻看神秘的Linux的內核。看到雙向鏈表時,覺得挺有意思的,此文記下。

作為眾多基礎數據結構中的一員,雙向循環鏈表在各種“教科書”中的實現是相當的標准和一致的。

Linux內核中的雙向循環鏈表學習 http://www.linuxidc.com/Linux/2012-07/65759.htm

大概就是下面這個樣子:

1 typedef struct node_tag{
2     //T data;
3     struct node_tag *prev;
4     struct node_tag *next;
5 }node;

當你需要某種類型的鏈表時,把數據成員之類的往節點裡塞就是了。比如菜譜鏈表,裡面就可以有宮爆雞丁,酸辣粉,地三鮮,水煮魚,麻辣雞翅。。。

嗯,當你需要另外一種鏈表時,接著如法炮制,只要功夫深,幾十上百也不是問題。有一部分人善於解決這類問題,它們叫做CP程序員(Copy-Paste),

不要問我為什麼知道。C++模板在這點上能實現通用特性,但不在本次內容之列了。

  有著極客精神的Linux,在內核中肯定不會像上面這麼做的。內核中有大量的數據結構需要使用雙向鏈表,諸如進程、模塊、文件。

難道要人去維護各種類型的雙向鏈表?而且還是不能復用的鏈表。我想沒多少人願意把時間花在這種事情上吧。維護一種通用的不就好了。

鏈表節點,作為一個“連接件”,最本質的內容就是把一些對象鏈接起來,至於對象內部存儲的數據,是可以不用知道的。

在include/linux/list.h文件中,就有使用這樣的一個"連接件“:

struct list_head {
    struct list_head *next, *prev;
};

和node_tag相比,少了數據部分。

list_head作為獨立變量時,充當的是鏈表頭的角色;如果作為結構體成員時,則是“連接件”的角色。

  在這樣的實現方式下,要獲得某種類型的鏈表,只需在宿主結構中聲明一個list_head成員,還可以任意的取名;

關鍵是,鏈表操作只需以list_head為對象進行實現;剩下唯一的問題是,在遍歷鏈表時,該如何獲取宿主結構的首地址?

畢竟鏈表是用來裝內容用的。這裡利用編譯器的一個小技巧就可以算出地址偏移

#define offsetof(s,m)   (size_t)&(((s *)0)->m)

有了list_head相對宿主結構首地址的偏移,和自身地址來個加減就可以得到宿主的首地址,接下該怎麼操作就怎麼操作了。

個人覺得這裡面有面向對象的意思。抽取出共同的“連接件” list_head,鏈表操作也以list_head為對象進行設計,

除了要具體訪問操作宿主結構之外,全部都是共性的東西。

Copyright © Linux教程網 All Rights Reserved