Linux核心學習之鏈表

來源:互聯網
上載者:User

文章參照任橋位Linux核心修鍊之道3.6節編寫。

在Linux核心中大量地方使用了鏈表這個資料結構。相信科班出身的學生或者自己學習過資料結構的同學都不陌生,不錯,他就是最簡單的線性結構——鏈表。不過,在核心當中,一般採用的都是迴圈雙聯表的資料結構。因為源碼有三百多行我就不貼在這裡,有興趣的去下載一下:http://download.csdn.net/detail/huiguixian/3889011。

1. 鏈表的定義

這個跟我們在課本上學習的一樣,相當簡單。包括了一個前項指標,和後項指標。是不是有點不對勁?不錯,竟然沒有資料域!不急,我們慢慢看。

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

沒有資料是核心鏈表的一大特色,因為他採用的方式比較特殊,他不是用鏈表來包含資料的,而是讓資料項目反回來包含鏈表的。剛開始多多少少有點難以理解,下面會解釋的。

2. 鏈表的的定義和初始化

(1)採用LIST_HEAD宏在編譯時間靜態初始化

#defineLIST_HEAD_INIT(name) { &(name), &(name) }#defineLIST_HEAD(name) \        struct list_head name =LIST_HEAD_INIT(name)

LIST_HEAD_INIT是宏定義,也就是說在定義的時候把他擴充一下就很容易理解了。比如初始化語句為

LIST_HEAD(event_list),可以理解為

struct list_headevent_list = { &event_list, &event_list }

結構體大家應該還沒有忘記吧,裡面有一條可以按照成員順序在定義時對其進行初始化,所以這句就很明顯了。目的是把next prev指標初始化指向它本身。

(2)採用INIT_LIST_HEAD函數在運行時動態初始化,這個目的一眼就看出來了,同上面一樣。

static inline voidINIT_LIST_HEAD(struct list_head *list){        list->next = list;        list->prev = list;}

3. 判斷鏈表是否為空白的操作,即是判斷是否指向自己本身而已

static inline intlist_empty(const struct list_head *head){        return head->next == head;}

4.插入操作,學過鏈表操作的都看得懂,看不懂的自己去學鏈表去。

static inline void__list_add(struct list_head *new,                             struct list_head *prev,                             struct list_head *next){        next->prev = new;        new->next = next;        new->prev = prev;        prev->next = new;}static inline voidlist_add(struct list_head *new, struct list_head *head){        __list_add(new, head,head->next);}static inline voidlist_add_tail(struct list_head *new, struct list_head *head){        __list_add(new, head->prev,head);}

5. 移動、刪除等等類似,主要講遍曆!遍曆的精彩部分在於鏈表是被資料包含著的,如何通過被包含的鏈表取出包含他的資料(有點拗口)

比如書上舉的那個例子:

struct list_head*tmp;struct usb_hub *hub;tmp =hub_event_list.next;hub = list_entry(tmp,struct usb_hub, event_list);

資料結構是usb_hub,裡麵包含著一個list_head資料項目,然後現在有一個list_head的鏈表hub_event_list,要取出裡麵包含hub_event_list.next的資料usb_hub。這就是上述代碼的功能。最重要的函數為list_entry,代碼如下:

#definelist_entry(ptr, type, member) \        container_of(ptr, type, member)

這個不用解釋,他調用的是container_of(ptr, type, member),直接看這個宏定義

#definecontainer_of(ptr, type, member) ({         \    const typeof( ((type *)0)->member )*__mptr = (ptr);    \    (type*)( (char *)__mptr - offsetof(type,member) );})#defineoffsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

這個看起來比較費力。需要一步步理解。首先,宏定義不是函數,宏定義的參數不受函數的限制,所以在list_entry和container_of的第二個參數都是以資料類型做參數的。另外GCC有一個對ISO C的擴充,就是他支援typeof操作,具體可以看這裡:http://blog.csdn.net/huiguixian/article/details/7045311 。主要看最後講解typeof。簡單來看他就是可以返回一個類型,基本可以用在你想用的任何時候。

接著上面的例子來解釋:

type為usb_hub,type *就是usb_hub*,0可以理解為NULL,也就是usb_hub->event_list就是((type *)0)->member。一整句就是定義了一個list_head類型的常量指標,指向了參數的event_list。然後下一步是通過計算位移量,讓這個指標減去這個位移量,即減去後的指標指向的可以看作是一個usb_hub的資料結構,至此就把usb_hub取出來了。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.