Linux內核之數據結構--鏈表

前言

Linux內核實現了一下常用的內建數據結構,主要有:

  • 鏈表
  • 隊列
  • 映射
  • 二叉樹

今天詳細學習一下鏈表的知識,鏈表是一種存放和操作可變數量元素(常稱爲節點)的數據結構。Linux內核的標準鏈表就是採用環形雙向鏈表形式實現的。

鏈表數據結構

傳統的鏈表是將數據存放在鏈表節點中;而Linux內核的方式與衆不同,它的鏈表節點只有兩個指針(prev和next),鏈表節點保存在用戶數據結構中。
鏈表代碼在頭文件< linux/list.h>中聲明,數據結構很簡單:

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

next指針指向下一個鏈表節點,prev指向上一個鏈表節點。然後這個鏈表節點list_head一般保存在數據的結構體內:

struct fox
{
    unsigned long      tail_length;         //尾巴長度
    unsigned long      weight;              //重量
    bool               is_fantastic;        //狐狸是否奇妙?
    struct list_head   list//鏈表節點存放在此處
}

這樣在以後對鏈表的操作都是針對鏈表節點list_head進行的,然後根據list_head就可以找到其所在的數據結構,這是通過list_entry()函數實現的:

list_entry(ptr,type,member);
/*
ptr是指向list_head類型的鏈表的指針
type是數據的結構體,struct fox
member是數據結構體中的一個域,類型爲list_head
函數的作用就是根據結構的成員指針找到其所在結構體的指針。
*/

聲明和初始化一個鏈表

Linux提供了兩種方式初始化鏈表。
一種是使用LIST_HEAD()這個宏:

#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
        struct list_head name = LIST_HEAD_INIT(name)

LIST_HEAD(fox_list);就是定義並初始化了名爲fox_list的鏈表,鏈表頭爲fox_list,其next、prev指針都指向自己。

另一種是先定義list_head指針變量,然後用INIT_LIST_HEAD()將其初始化爲鏈表:

struct fox *red_fox;
red_fox=kmalloc(sizeof(*red_fox),GFP_LERNEL);
red_fox->tail_length=40;
red_fox->weigth=6;
red_fox->is_fantastic=false;
INIT_LIST_HEAD(&red_fox->list);//注意參數爲指針

添加和刪除節點

向鏈表中添加一個節點:

list_add(struct list_head *new, struct list_head *head);
例:
list_head(&fox->list, &fox_list);
//fox是新建的數據結構體,fox_list是之前初始化的鏈表

把節點增加到鏈表尾:

list_add_tail(struct list_head *new, struct list_head *head);

從鏈表中刪除一個節點:

list_del(struct list_head *list);
例:
list_del(&fox->list);

注意,該刪除操作並不會釋放list或包含list的結構體所長用的內存。僅僅是將其從鏈表中移除。
從鏈表中刪除一個節點並對其初始化:

list_del_init(struct list_head *list);

把節點從一個鏈表移動到另一個鏈表:

list_move(struct list_head *list, struct list_head *head);
//把list項從鏈表中移除,並添加到另一個鏈表的head節點後面

把節點從一個鏈表移動到另一個鏈表的末尾:

list_move_tail(struct list_head *list, struct list_head *head);

檢查鏈表是否爲空:

list_empty(struct list_head *head);

若鏈表爲空返回非0值,否則返回0。
合併兩個鏈表:

list_splice(struct list_head *list, struct list_head *head);
list_splice_init(struct list_head *list, struct list_head *head);
//合併後初始化原來的鏈表

將list指向的鏈表插入到指定的鏈表head節點後面。

遍歷鏈表

最簡單的方法就是使用list_for_each()宏遍歷鏈表,再通過list_entry()獲取完整數據結構體
例:

struct list_head *p;
struct fox *f;
list_for_each(p, &fox_list)
{
    f=list_entry(p, struct fox, list);
}

以上兩個函數可以合併爲一個:list_for_each_entry()

struct fox *f;
list_for_each_entry(f, &fox_list, list)
{
    //f就遍歷了所有的數據結構體;
}

反向遍歷鏈表:

list_for_each_entry_reverse(pos, head, member);

遍歷的同時安全刪除節點:

list_for_each_entry_safe(pos, next, head, member);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章