Linux內核list/hlist解讀


轉自:http://blog.chinaunix.net/uid-20671208-id-3763131.html

 

目錄

1. 前言 2

2. 通用宏 2

2.1. typeof 2

2.1.1. 定義 3

2.1.2. 用途 3

2.1.3. 示例 3

2.2. offset_of 3

2.2.1. 定義 3

2.2.2. 作用 3

2.2.3. 原理 3

2.2.4. 示例 3

2.3. container_of 4

2.3.1. 定義 4

2.3.2. 作用 4

2.3.3. 示例 4

2.4. prefetch 4

2.4.1. 定義 4

2.4.2. 作用 4

3. list 5

3.1. list結構 5

3.1.1. 定義 5

3.1.2. 作用 5

3.1.3. 解讀 5

3.1.4. 示例 5

3.2. 遍歷方向 6

3.3. list_entry 6

3.3.1. 定義 6

3.3.2. 作用 7

3.4. list_for_each 7

3.4.1. 定義 7

3.4.2. 作用 7

3.4.3. 示例 7

3.5. __list_for_each 8

3.5.1. 定義 8

3.5.2. 作用 8

3.6. list_for_each_prev 8

3.6.1. 定義 8

3.6.2. 作用 8

3.7. list_for_each_safe 8

3.7.1. 定義 8

3.7.2. 作用 9

3.7.3. 區別 9

3.7.4. 示例 9

3.8. list_for_each_entry 9

3.8.1. 定義 9

3.8.2. 作用 10

3.8.3. 區別 10

3.8.4. 完整示例 10

3.9. list_for_each_entry_safe 12

3.9.1. 定義 12

3.9.2. 作用 13

3.10. list_for_each_entry_reverse 13

3.10.1. 定義 13

3.10.2. 作用 13

3.11. list_for_each_entry_continue 13

3.11.1. 定義 13

3.11.2. 作用 13

3.11.3. 區別 13

3.12. list_for_each_safe_rcu 14

4. hlist(hash list) 14

4.1. hlist(hash list)結構 14

4.1.1. 簡述 14

4.1.2. 定義 14

 

1. 前言

Linux內核實現了一批優雅而功能強大的雙向循環列表操作宏,它們位於/usr/include/linux/list.h(請注意直接#include會報編譯錯誤),這些宏可以直接扣出來,在需要時使用。

2. 通用宏

2.1. typeof

請注意typeof並不是一個宏,而是GCC的一個內建操作符。

2.1.1. 定義

typeof(variable)

2.1.2. 用途

得到變量variable的類型,這個類型可以用來定義同類型的其它變量。

2.1.3. 示例

int m = 1;

typeof(m) n = m; // 等同於int n = m;

2.2. offset_of

2.2.1. 定義

// type:結構體類型

// member:結構體成員

#define offsetof(type, member) \

((size_t) &((type *)0)->member)

2.2.2. 作用

計算出一個結構體type中,指定成員member在該結構體中的位置。

2.2.3. 原理

(type *)0是一個類型為type的指針,其指針地址為0x0。&((type *)0)->member)是得到成員member的地址,由於結構體本身的地址為0x0,所以成員member的地址亦為member在該type中的偏移位置。

2.2.4. 示例

假設有如下一個結構體:

#pragma pack(4)

struct X

{

int32_t  a;

int32_t  b;

int32_t  c;

};

#pragma pack()

那么offsetof(Struct X, b)的值等於4。

2.3. container_of

2.3.1. 定義

// ptr:結構體成員member的地址

// type:結構體類型

// member:結構體成員member

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

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

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

2.3.2. 作用

通過結構體一個成員的地址,得到該結構體的地址。

2.3.3. 示例

定義結構體變量:

struct X x;

那么container_of(&x.b, struct X, b)的值將和&x相等。

2.4. prefetch

2.4.1. 定義

// x:需要預讀的變量

#define prefetch(x) __builtin_prefetch(x)

2.4.2. 作用

這里使用到的__builtin_prefetch是GCC內建函數,它的作用是告訴cpu括號中的x可能馬上就要用到,以便cpu預取一下,這樣可以提高效率。

3. list

3.1. list結構

3.1.1. 定義

struct list_head

{

        struct list_head *next;  // 指向下一個結點

        struct list_head *prev;  // 指向前一個結點

};

這個定義雖然簡單,但卻是核心。

3.1.2. 作用

用來實現通用的雙向鏈表,只包括前后指針,並不包含數據成員。

3.1.3. 解讀

struct list_head有點類似於C++基類:

class list_head

{

public:

list_head()

: next(NULL)

, prev(NULL)

{

}

 

public: // 實際使用時,應當改為private,采用成員函數操作方式

list_head* next;

list_head* prev;

};

3.1.4. 示例

在C++語言中,基類是子類實例的一個子對象。在設計模式中,有template模式與strategy兩個可以相互轉化的模式,template模式采用的是繼承,而strategy模式采用的是子對象方式。

由於在C語言中,沒有繼承的概念,所以只能采用子對象的方式。因此需要這樣使用:

struct MyData

{

// 它在結構體中出現的順序沒有要求,

// 因為可以通過下面將要介紹的container_of宏來取得它所歸屬結構體的地址

struct list_head list;

char* data;

};

如果是在C++中,則:

class MyData: pubic list_head

{

private:

char* _data;

};

在C++中,如果:

list_head* data = new MyData;

則可以這樣:

MyData* mydata = dynamic_cast<mydata*>(data);

即可由基類子對象的地址,來得到子類對象的地址。而在C語言中,則需要借助container_of宏:container_of(data, MyData, list);

3.2. 遍歷方向

 

 

 

雙向循環列表頭尾相連,有2個遍歷方向:

1) 順時針

2) 逆時針

3.3. list_entry

3.3.1. 定義

// ptr:結構體成員member的地址

// type:結構體類型

// member:結構體成員member

#define list_entry(ptr, type, member)  \

container_of(ptr, type, member)

3.3.2. 作用

從list_entry的定義可以看出,它等同於container_of,即通過結構體type一個成員member的地址ptr,得到該結構本的地址。

3.4. list_for_each

3.4.1. 定義

// pos:指向當前結點的指針

// head:指向雙向鏈表的頭的指針

#define list_for_each(pos, head)  \

        for (pos = (head)->next;  \

prefetch(pos->next), pos != (head);  \

                pos = pos->next)

這里的prefetch(pos->next)不是必須的,只是為提升效率。

3.4.2. 作用

以順時針方向遍歷雙向循環鏈表,由於是雙向循環鏈表,所以循環終止條件是“pos != (head)”。在遍歷過程中,不能刪除pos(必須保證pos->next有效),否則會造成SIGSEGV錯誤。

3.4.3. 示例

struct list_head cur;

struct list_head list;

 

list_for_each(cur, &list)

{

// 使用cur

}

3.5. __list_for_each

3.5.1. 定義

// pos:指向當前結點的指針

// head:指向雙向鏈表的頭的指針

#define __list_for_each(pos, head) \

        for (pos = (head)->next;  \

pos != (head);  \

pos = pos->next)

3.5.2. 作用

功能和list_for_each相同,即以順時針方向遍歷雙向循環鏈表,只不過省去了prefetch(pos->next)。在遍歷過程中,不能刪除pos(必須保證pos->next有效),否則會造成SIGSEGV錯誤。

3.6. list_for_each_prev

3.6.1. 定義

// pos:指向當前結點的指針

// head:指向雙向鏈表的頭的指針

#define list_for_each_prev(pos, head) \

        for (pos = (head)->prev; \

prefetch(pos->prev), pos != (head); \

                pos = pos->prev)

3.6.2. 作用

以逆時針方向遍歷雙向循環鏈表。在遍歷過程中,不能刪除pos(必須保證pos->prev有效),否則會造成SIGSEGV錯誤。

3.7. list_for_each_safe

3.7.1. 定義

// pos:指向當前結點的指針

// head:指向雙向鏈表的頭的指針

// n:臨時用來保存指向pos的下一個結點的指針

#define list_for_each_safe(pos, n, head) \

        for (pos = (head)->next, n = pos->next;  \

pos != (head); \

                pos = n, n = pos->next)

3.7.2. 作用

以順時針方向遍歷雙向循環鏈表。在遍歷過程中,允許刪除pos,因為每次都保存了pos->next。

3.7.3. 區別

list_for_each_safe(pos, n, head)

list_for_each(pos, head)

遍歷中,可刪除pos

遍歷中,不可刪除pos

3.7.4. 示例

struct list_head cur;

struct list_head tmp; // 臨時用來保存cur的next

struct list_head list;

 

list_for_each_safe(cur, tmp, &list)

{

// 使用pos

list_del(pos); // 將pos從鏈表中剔除

delete pos; // 刪除pos

}

3.8. list_for_each_entry

3.8.1. 定義

為方便講解,假設有:

struct MyNode

{

struct list_head list;

};

實際中,應當將MyNode替代成需要的類型的,但不管叫什么,總是聚合了struct list_head。

 

// pos:指向當前結點(在這里,類型為MyNode)的指針

// head:指向雙向鏈表list_head的頭的指針

// member:list_head類型在MyNode中的成員(在這里為list)

#define list_for_each_entry(pos, head, member)                          \

        for (pos = list_entry((head)->next, typeof(*pos), member);      \

             prefetch(pos->member.next), &pos->member != (head);        \

             pos = list_entry(pos->member.next, typeof(*pos), member))

3.8.2. 作用

以順時針方向遍歷雙向循環鏈表。它和list_for_each一樣是做鏈表遍歷,但pos的類型不一樣。在list_for_each中,pos類型是“struct list_head*”,而在list_for_each_entry是typeof(*pos)類型。

3.8.3. 區別

list_for_each_entry(pos, head, member)

list_for_each(pos, head)

遍歷鏈表,pos和head是typeof(*pos)類型

遍歷鏈表,pos和head是struct list_head類型

3.8.4. 完整示例

// 這個示例,是可以編譯成功,並可運行查看效果

// 假設文件名為x.cpp,則編譯方法為:g++ -g -o x x.cpp

#include 

#include 

 

struct list_head {

        struct list_head *next, *prev;

};

 

static inline void INIT_LIST_HEAD(struct list_head *list)

{

        list->next = list; 

        list->prev = list; 

}

 

static inline void __list_add(struct list_head *inserted,

                              struct list_head *prev,

                              struct list_head *next)

{

        next->prev = inserted;

        inserted->next = next; 

        inserted->prev = prev; 

        prev->next = inserted;

}

 

static inline void list_add_tail(struct list_head *inserted, struct list_head *head)

{

        __list_add(inserted, head->prev, head);

}

 

#define prefetch(x) __builtin_prefetch(x)

 

#define offsetof(type, member) \

        ((size_t) &((type *)0)->member)

 

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

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

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

 

#define list_entry(ptr, type, member) \

        container_of(ptr, type, member)

 

#define list_for_each(pos, head) \

        for (pos = (head)->next; prefetch(pos->next), pos != (head); \

                pos = pos->next)

 

#define list_for_each_entry(pos, head, member)                          \

        for (pos = list_entry((head)->next, typeof(*pos), member);      \

             prefetch(pos->member.next), &pos->member != (head);        \

             pos = list_entry(pos->member.next, typeof(*pos), member))

 

// 以上除#include外的代碼,是從/usr/include/linux/list.h直接抄過來的,

// 但請注意直接#include ,將無法編譯通過

 

// 定義一個結構體

struct MyData

{

public:

        struct list_head list; // 功能上相當於從list_head繼承

        char* data;

};

 

int main()

{

 

        MyData* head = new MyData; // 必須有個空閑頭結點

 

        MyData* data1 = new MyData; // 第一個結點

        data1->data = reinterpret_cast<char*>(0x01);

 

        MyData* data2 = new MyData; // 第二個結點

        data2->data = reinterpret_cast<char*>(0x02);

 

        INIT_LIST_HEAD(&head->list);

        list_add_tail(&data1->list, &head->list);

        list_add_tail(&data2->list, &head->list);

 

        // 請注意cur1和cur2的數據類型

        MyData* cur1 = NULL;

        list_head* cur2 = NULL; 

    // 請注意下面兩個循環的區別,它們是互通的

        list_for_each_entry(cur1, &head->list, list)

        {

                printf("%p\n", cur1->data);

        }

        list_for_each(cur2, &head->list)

        {       

                MyData* dd = container_of(cur2, MyData, list);  

                printf("%p\n", dd->data);

        }

 

        return 0;

}

 

上段代碼運行后,將輸出以下四行信息:

0x1

0x2

0x1

0x2

3.9. list_for_each_entry_safe

3.9.1. 定義

// pos:指向當前結點(在這里,類型為MyNode)的指針

// head:指向雙向鏈表list_head的頭的指針

// member:list_head類型在MyNode中的成員(在這里為list)

// n:用來臨時存儲pos的下一個結點(類型和pos相同)

#define list_for_each_entry_safe(pos, n, head, member)                  \

        for (pos = list_entry((head)->next, typeof(*pos), member),       \

                n = list_entry(pos->member.next, typeof(*pos), member);  \

              &pos->member != (head);                                    \

              pos = n, n = list_entry(n->member.next, typeof(*n), member))

3.9.2. 作用

list_for_each_entry的可刪除結點版本。

3.10. list_for_each_entry_reverse

3.10.1. 定義

#define list_for_each_entry_reverse(pos, head, member)                  \

        for (pos = list_entry((head)->prev, typeof(*pos), member);      \

             prefetch(pos->member.prev), &pos->member != (head);        \

             pos = list_entry(pos->member.prev, typeof(*pos), member))

3.10.2. 作用

作用和list_for_each_entry相同,只不過它是逆時針方向遍歷。

3.11. list_for_each_entry_continue

3.11.1. 定義

#define list_for_each_entry_continue(pos, head, member)                 \

        for (pos = list_entry(pos->member.next, typeof(*pos), member);  \

             prefetch(pos->member.next), &pos->member != (head);        \

             pos = list_entry(pos->member.next, typeof(*pos), member))

3.11.2. 作用

以順時針方向,從pos點開始遍歷鏈表。

3.11.3. 區別

list_for_each_entry_continue(pos, head, member)

list_for_each_entry(pos, head, member)

從pos點開始遍歷鏈表

從頭開始遍歷鏈表

傳入的pos不能為NULL,必須是已經指向鏈表某個結點的有效指針

對傳入的pos無要求,可以為NULL

3.12. list_for_each_safe_rcu

這個牽涉到RCU(Read-Copy-Update),在這里不詳細講解了,如有興趣,請參考《玩轉多線程編程.ppt》一文。

4. hlist(hash list)

4.1. hlist(hash list)結構

4.1.1. 簡述

hlist也是 一種雙向鏈表,但不同於list_head,它的頭部只有一個指針,常被用作哈希表的bucket數組,這樣就可減少哈希bucket一半的內存消耗。

4.1.2. 定義

struct hlist_head {

        struct hlist_node *first; 

};

 

struct hlist_node {

        struct hlist_node *next, **pprev;

};


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM