歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> Linux教程 >> 深入淺出Linux之內核數據結構

深入淺出Linux之內核數據結構

日期:2017/2/28 16:00:07   编辑:Linux教程
內核使用的數據結構有雙向鏈表,單向鏈表和hash鏈表。另外,基樹和紅黑樹也是內核使用的數據結構。實際上,這也是程序代碼中通常使用的數據結構,一些偏僻難的數據結構並不常見。

1. container

container是linux很重要的一個概念。有了container方法,才能實現對對象的封裝。

分析一下container方法。

======================================================================

#define container_of(ptr, type, member) ({ \

const typeof( ((type *)0)->member ) *__mptr = (ptr); \

(type *)( (char *)__mptr - offsetof(type,member) );})

這個方法巧妙的實現了通過結構的一個成員找到整個結構的地址。有了container方法,list才

成為了一個通用的數據結構,通過container方法,還可以實現內核的封裝,程序之間不暴露

內部的數據。

2. 雙向鏈表list

List定義在/include/linux目錄下。首先看看list的結構定義:

struct list_head {

struct list_head *next, *prev;

};

List是雙向鏈表的一個抽象,list庫提供了list_entry,使用了container方法,通過container 可以從list找到整個數據對象,這樣list就成為了一種通用的數據結構。

======================================================================

#define list_entry(ptr, type, member) \

container_of(ptr, type, member)

內核定義了很多對list結構操作的內聯函數和宏,計有:

  • LIST_HEAD:定義並初始化一個list鏈表
  • list_add_tail:加一個成員到鏈表尾
  • list_del:刪除一個list成員

  • list_empty:檢查鏈表是否為空
  • list_for_each:遍歷鏈表。得到鏈表指針。
  • list_for_each_safe:遍歷鏈表。和list_for_each區別是可以刪除遍歷的成員
  • list_for_each_entry:遍歷鏈表,通過container方法返回結構指針。

3. hash list

Hash list和雙向鏈表list很相似,它適用於hash表。看一下hash list的head定義:

struct hlist_head {

struct hlist_node *first;

};

和通常的list比較,hlist頭只有一個指針,這樣就節省了一個指針的內存。如果hash表非常

龐大,那麼每個hash 表頭節省一個指針,整個hash表節省的內存就很可觀了。這就是內核

中專門定義hash list的原因。

Hash list庫提供的函數和list很相似,計有:

l HLIST_HEAD:定義並初始化一個hash list鏈表頭

l hlist_add_head:加一個成員到hash鏈表頭

l hlist_del:刪除一個hash list成員

l hlist_empty:檢查hash 鏈表是否為空

l hlist_for_each:遍歷hash 鏈表。

l hlist_for_each_safe:遍歷鏈表。和hlist_for_each區別是可以刪除遍歷的成員

l hlist_for_each_entry:遍歷hash 鏈表,通過container方法返回結構指針

4. 單向鏈表

內核中,沒有提供單向鏈表的定義。但實際上,有多處使用了單向鏈表的概念。

======================================================================

for (i = 0, p -= n; i < n; i++, p++, index++) {

struct probe **s = &domain->probes[index % 255];

while (*s && (*s)->range < range)

s = &(*s)->next;

p->next = *s;

*s = p;

}

上面的例子是加入一個新的字符設備到系統的表裡面。在系統的字符設備表裡,

probe結構其實就是單向鏈表。這種結構在內核中應用很廣泛。

5. red-black tree

紅黑樹位於/lib/rbtree.c文件。紅黑樹是一種自平衡的二叉樹。實際上,紅黑樹可以看做是折半查找。我們知道,排序的快速做法是取隊列的中間值比較,然後在剩下的隊列中再次取中間值比較,迭代進行直到找到最合適的位置。紅黑樹實際就是實現了這種算法。

那麼紅黑樹裡面的“紅黑”代表什麼意思?紅黑的顏色處理是避免樹的不平衡。舉個例子,如果1,2,3,4,5五個數字依次插入一顆紅黑樹,那麼五個值都落在了樹的右側,如果6再插入這顆紅黑樹,那麼需要比較五次。“紅黑規則”就要將樹旋轉,樹的根部要落在3這個節點,只需要比較兩次,這樣就避免了樹的不平衡導致的問題。

內核中的io調度算法和內存管理中都使用了紅黑樹。紅黑樹有很多介紹的文章,讀者可以結合代碼分析一下。

6. radix tree

內核提供了一個基樹庫,代碼在/lib/radix-tree.c文件。基樹是一種空間換時間的數據結構,

通過空間的冗余減少了時間上的消耗。用一張圖來分析:

圖中顯示,元素空間總共為256,但元素個數不固定。那麼如果用數組存儲,好處是插入查找只用一次操作,但是存儲空間需要256,這是不可思議的。如果用鏈表存儲,存儲空間節省了,但是極限情況下查找操作次數等於元素的個數。而采用一顆高度為2的基樹,第一級最多16個冗余結構,代表元素前四位的索引。第二級代表元素後四位的索引。那麼只要兩級查找就可以找到特定的元素,而且只有少量的冗余數據。圖中假設只有一個元素10001000,那麼只有樹的第一級有元素,而且樹的第二級只有1000這個節點有子節點,其它節點都不必分配空間。這樣既可以快速定位查找,也減少了冗余數據。

基樹很適合存儲稀疏的數據,內核中文件的頁cache就采用了基樹。關於基樹的文章很多,讀者可以結合代碼分析一下。

Copyright © Linux教程網 All Rights Reserved