標籤:核心 資料結構
我個人比較喜歡學習資料結構,而Linux核心中實現的資料結構會是我們去學習、理解和應用資料結構的一個很好途徑。這裡介紹核心中廣泛應用的四種資料結構:鏈表、隊列、映射和二叉樹。
鏈表:
Linux核心講求高效精簡,所以有時需要我們動態去建立和分配記憶體,這時就要藉助鏈表,我們根據實際情況分配記憶體後,只需修改鏈表的指標,仍能索引到剛分配的記憶體區。鏈表分單向鏈表、雙向鏈表和迴圈鏈表。
struct list_element {void *data;struct list_element *next; /* 指向下一個元素的指標 */};
struct list_element {void *data;struct list_element *next; /* 指向下一個元素的指標 */struct list_element *prev; /* 指向前一個元素的指標 */};
鏈表最後一個元素的next指標不為NULL,而是指向鏈表首元素。迴圈鏈表中也分單向和雙向。
因為迴圈雙向鏈表提供了最大的靈活性(沒有具體例子的對比,暫時還看不出來,希望懂的朋友們留下評論:),所以Linux核心的標準鏈表就是採用迴圈雙向鏈表。另外,如果需要隨機訪問資料,一般不使用鏈表,使用鏈表的理想情況是需要遍曆所有資料或需要動態加入和刪除資料時。不過Linux核心中的鏈表可不像上面例子那樣簡單和原始,它不是將資料結構塞入鏈表,而是將鏈表節點塞入資料結構。
struct list_head {struct list_head *next;struct list_head *prev;};
其實這就是所謂的鏈表節點,那它是如何塞入資料結構的呢?當然是作為資料結構的成員:
struct fox {unsigned long tail_length;unsigned long weight;bool is_fantastic;struct list_head list; /* 所有fox結構體形成鏈表 */};
也就是list不再是指向一個fox結構體,而是用.next和.prev指向前後兩個list_head結構體。真奇怪,這樣的話我們需要知道所指向的兩個list_head結構體分別屬於哪兩個fox結構體,這需要藉助宏container_of(),這樣我們可以找到這個list_head結構體的父結構(也就是所屬的fox結構體)中包含的任何變數。為什麼要這樣構造鏈表,希望懂的朋友們留下評論:)
隊列:
在核心編程中,我們經常遇到生產者——消費者模型,這時隊列是最理想的資料結構,生產者將資料放入隊列,消費者則隨後取出,先到先得。
映射:
映射指的是一種做法:有一個由唯一鍵組成的集合,每個鍵關聯一個特定的值,由鍵得到值得這種關係我們稱之為映射。但用什麼資料結構來實現,一般採用散列表(Hash)和自平衡二叉搜尋樹。也許散列表是我們最熟悉的,構造散列函數,輸入唯一鍵,得到對應值。但其實更多的時候採用二叉樹,Linux核心就是如此。Linux核心中用idr維護一個自訂的映射,用
int idr_get_new(struct idr *idp, void *ptr, int *id);
將id(鍵)和ptr(值)的映射注入到idr中。而尋找操作如下:
void *idr_find(struct idr *idp, int id);
根據id找到對應的指標ptr。
二叉樹:
具體紅/黑樹狀結構的介紹參見我的博文紅/黑樹狀結構(RED-BLACK TREES)基本概念核心中每個i節點都有自己的rbtree,以關聯在檔案中的頁位移,這是紅/黑樹狀結構在Linux中的一個應用。