list_head使用介紹

list_head結構的介紹


在list_head結構定義裏,它是一個double linked list的結構。 如下::

kernel/include/linux/types.h:

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

有的人可能看到這樣的結構會覺得很奇怪這樣的結構可以存放資料嗎? 當然是不行的,因爲這個結構根本是拿來讓人當資料存的。 首先, 我們先來看看兩個macro,

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

=========>
{

    #define LIST_HEAD_INIT(name) { &(name), &(name) }

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

 #define INIT_LIST_HEAD(ptr) do { /
(ptr)->next = (ptr); (ptr)->prev = (ptr); /
} while (0)

這兩個macro在Kernel裏也算蠻常出現的, 是用來將list_head做初始化的,它的初始化就是將next和prev這兩個field設爲跟結構的地址相同。 所以, 如果我們在程序裏看到這樣的代碼, 它的意思就是定義一個list_head結構的變量hello,並將prev和next都設成hello的地址。

LIST_HEAD(hello)

因此, 如果要檢查這個list是否是空的, 只要檢查hello.next是否等於&hello就可以了。事實上, Linux也提供了一個叫list_empty()的函數來檢查list是否爲空的。

kernel/include/linux/list.h

/**
 * list_empty - tests whether a list is empty
 * @head: the list to test.
 */
static inline int list_empty(const struct list_head *head)
{
    return head->next == head;
}

現在我們來介紹如何加入或刪除list_head到上面的hello串行裏。 Linux提供了2個函數來做這些事, 分別是list_add()和lis_del()。 這兩個函數的定義都放在 裏, 而且其程序代碼也都很簡單,只是單純double linked list的串接和刪除而已。

kernel/include/linux/list.h

/**
 * list_add - add a new entry
 * @new: new entry to be added
 * @head: list head to add it after
 *
 * Insert a new entry after the specified head.
 * This is good for implementing stacks.
 */
static inline void list_add(struct list_head *new, struct list_head *head)
{
    __list_add(new, head, head->next);
}


static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
	__list_add(new, head->prev, head);
}

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;
}

list_add 將這個新加入的new放在了list的head之後的第一個元素,head->next=new;

而list_add_tail將新加入的new放在了list的尾部,讓head->prev=new

static inline void list_del(struct list_head *entry)
{
	__list_del(entry->prev, entry->next);
	entry->next = LIST_POISON1;
	entry->prev = LIST_POISON2;
}

/*
 * Delete a list entry by making the prev/next entries
 * point to each other.
 *
 * This is only for internal list manipulation where we know
 * the prev/next entries already!
 */
static inline void __list_del(struct list_head * prev, struct list_head * next)
{
	next->prev = prev;
	prev->next = next;
}

list_del將list中指定的enry刪除,同時將entry的prev 和entry-->next link在一起


關於list_head, 其實最重要的應該是它提供的這個macro。

#define list_entry(ptr, type, member) /
((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))

我們現在來做個實驗, 相信各位會更容易瞭解這個macro的。 請看一下下面這段程序碼。

struct HelloWorld {
   int x, y;
   struct list_head list;
} hello;

假設int是4個byte。 那麼以下這一行會得到8, 如圖5所示

(unsigned long) (&((struct HelloWorld *)0)->list)

有的人會對這一行程序感到奇怪, (struct HelloWorld*)0不就是一個NULL的指標嗎? 怎麼可以用0->list去參考list這個欄位呢? 難道不怕造成segmentation fault嗎? 請注意一下, 我們在0->list的前面還加上了一個&。 如果沒有&, 那上面這一行就會segmentation fault了。 如果你加上了&, 那就沒問題了。 Segmentation fault通常是去參考不合法的記憶體地址內容所造成的, 如果我們加上了&就表示我們沒有要去參考這個不合法地址的內容,我們只是要那個field的地址而已, 因此, 不會造成segmentation fault。 其實, 結構的配置在記憶體裏是連續的。 所以, 如果我們去讀取某個filed時,像&hello->list。 會先取得hello變l量的地址, 再然後再計算HelloWorld結構裏list所在的offset, 再將hello的地址加上list的offset,求得list真正的地址。 然後再去讀list的內容。 這是compiler幫我們做的。 那我們現在就來看看上面那一行究竟是什麼意思。 首先, 我們先把上面那一行想象成下面這個樣子。

ptr = 0;
(unsigned long) (&((struct HelloWorld *)ptr)->list)

這樣是不是容易懂了嗎, 就是要取得&ptr->list的地址而已。所以, 如果ptr是100的話, 那會得到100+8=108。 因爲前面有二個int, 每一個int是4個byte。 經過轉型, 就得到了(unsigned long)型的108。 如果ptr是0的話, 那同理, 我們會得到0+8=8。 也就是這個filed在HelloWorld結構裏的offset。

現在, 如果我們已經知道了list在HelloWorld結構中的offset,而且我們現在也知道hello這個變量裏list的地址的話, 那有沒有辦法得到hello本身的地址呢? 可以的, 就 如果我們知道list的地址, 只要將list的地址減8就可以知道了hello的地址了嘛。

struct list_head *plist = &hello.list;
printf( "&hello = %x\n", (char*)plist - (unsigned long) 8 ));

而這種方式就是list_head的用法, 它是專門用來當作別的結構的filed,只要我們得到這個list_head的在結構中的偏移和該list_head的指針, 我們可以很輕易的算出包含此list_head的結構地址.


/**
 * list_first_entry - get the first element from a list
 * @ptr:	the list head to take the element from.
 * @type:	the type of the struct this is embedded in.
 * @member:	the name of the list_struct within the struct.
 *
 * Note, that list is expected to be not empty.
 */
#define list_first_entry(ptr, type, member) \
	list_entry((ptr)->next, type, member)

list_first_entry 找到第一個entry所對應的結構,type是該結構體所對應的類型


結合博客 http://blog.csdn.net/billpig/article/details/5880895 和自己所看的內容

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