Hi,欢迎来到嵌入式培训高端品牌 - 华清远见教育科技集团<北京总部官网>,专注嵌入式工程师培养15年!
当前位置: > 华清远见教育科技集团 > 嵌入式学习 > 讲师博文 > Linux 内核中神一般的list
Linux 内核中神一般的list
时间:2017-01-06作者:华清远见

一、实例内容与目标

众所周知,linux内核中有一个通用的双向循环list链表,为什么称他为通用的呢?首先我们还是来看一下内核中list中的结点是怎样定义的:

从上面我们的定义我们看到一个奇怪的地方,就是这个结构和我们以前接触过链表结点有一个不一样的地方,就是没有数据部分。呵呵,也许正因为这样它被称为神一般的list吧。

本文,我们只分析两点:

1.它是总样做到通用的
        2.它的一些基本操作函数
        3.我们先来看看,它设计到的一个非常重要的宏list_entry(ptr,type,member)

从上面可知,这个宏终等价与container_of().这个已经在揭开container_of神秘面纱译文中已经讲解过了,这里就不在过多的介绍了。

终呢,我们可以通过一个结构体变量的一个成员指针,找到这个结构体变量的首地址。呵呵,在这里你是否想到,如果我们把struct list_head作为一个结构体的一部分(实际上它也是通过这样做到通用的),在这里我们假设我们的结构体造型如下:

Struct test
        {
                Struct list_head list;
                .......
        };

现在假如我们有好多个struct test结果体变量,我们如何把这些结构体变量连起来呢?这个时候我们的神一般的list就派上用场了。

在linux内中,提供了下方法,对list进行操作

插入到链表头:
        void list_add(struct list_head *new, struct list_head *head);

插入到链表尾:
        void list_add_tail(struct list_head *new, struct list_head *head);

删除链表节点:
        void list_del(struct list_head *entry);

将节点移动到另一链表:
        void list_move(struct list_head *list, struct list_head *head);

将节点移动到链表尾:
        void list_move_tail(struct list_head *list,struct list_head *head);

判断链表是否为空,返回1为空,0非空
        int list_empty(struct list_head *head);

把两个链表拼接起来:
        void list_splice(struct list_head *list, struct list_head *head);

取得节点指针:
        #define list_entry(ptr, type, member) \
        ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))

遍历链表中每个节点:
        #define list_for_each(pos, head) \
        for (pos = (head)->next, prefetch(pos->next); pos != (head); \
                pos = pos->next, prefetch(pos->next))

逆向循环链表中每个节点:
        #define list_for_each_prev(pos, head) \
        for (pos = (head)->prev, prefetch(pos->prev); pos != (head); \
                pos = pos->prev, prefetch(pos->prev))

下面我们看一个实际的例子,感受一下:

上面的这个列子,只做了一个简单的操作,就是自己实现了一个双向循环链表的插入操作。

在主函数中,先是插入元素,然后打印元素。当然内核还提供了好多关于list的操作方法,读者可以自己去挖掘。

发表评论
评论列表(网友评论仅供网友表达个人看法,并不表明本站同意其观点或证实其描述)