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 和自己所看的内容

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