学习 linux 链表使用方法

最近看了几个 RTOS 内核文件,发现内核用到的链表都使用宏来操做。

看内容应该是借鉴了 linuxlist.h 代码。 好奇研究 linux 的链表代码,学习记录下。

下面是我常用的链表结构定义方式:

typedef struct _dlink_node {
    void *pData;                    // 存放用户数据对象指针
    struct _dlink_node *ptPre;
    struct _dlink_node *ptNext;
} dlink_node_t;

typedef struct _dlink_list {
    dlink_node_t *ptHead;
    int32_t nCount;
} dlink_list_t;

使用方式一般是先定义一个链表控制块对象 dlink_list_t list , 所有的节点增删改查都是针对这个链表对象进行操作。

比如插入一个数据的伪代码如下:

dlink_node_t node = {.pData = &UserData};

dlink_list_insert(&list, 1, &node);

然而,linux 的链表实现方式与此不同,没有定义的链表管理控制块 list。而是把 dlink_node_t 这样的节点直接定义到目标对象中。

通过 dlink_node_t 中的 prenext 指针直接建立对象之间的联系。

注:可以直接在 linxu 主机中查看源码文件,我是用的腾讯的虚拟云主机

[root@lzlinks ~]# vim /usr/src/kernels/3.10.0-957.5.1.el7.x86_64/include/linux/types.h
[root@lzlinks ~]# vim /usr/src/kernels/3.10.0-957.5.1.el7.x86_64/include/linux/list.h

linux 中,链表结构体定义在 types.h 文件中, 定义如下:

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

可以看到,Linux 内核中定义的是一个仅仅只有两个指针,没有用户数据的双向循环链表结构体。

用户需要把 list 结构体内嵌到自己的数据对象中,通过 list.h 提供的链表操作宏,完成链表的初始化和增删改查操作。

struct user_object {
    struct list_head    list_node;
    int                 data;
}

/**
    \breief: usr object list

                  object               object
  ...+++ ->next  +++++++++  -> next  +++++++++  -> next +++...
       +         +  list +           +  list +          +
  ...+++ <- pre  +++++++++  <- pre   +++++++++  <- pre  +++...
       +         +  data +           +  data +          +
  ...+++         +++++++++           +++++++++          +++...

*/

linux 链表的常用API:

void list_add(struct list_head *new, struct list_head *head);
void list_add_tail(struct list_head *new, struct list_head *head);

// 各种宏定义,都是直接操作链表节点
#define list_for_each(pos, head) \
           for (pos = (head)->next; pos != (head); pos = pos->next)

#define list_for_each_entry(pos, head, member)                          \
           for (pos = list_entry((head)->next, typeof(*pos), member);   \
                &pos->member != (head);    \
                pos = list_entry(pos->member.next, typeof(*pos), member))

这样做的好处是:

* 链表可以出现在任何位置 

* 可以给链表任意命名  

* 一个结构体可以属于多个链表,这是 Linux 内核的特性要求数据结构必须做到的

缺点是:

* 统计链表中节点的数量,需要遍历。当然也可以定义一个变量,作为计数器。

* 用户操作不当可能会破坏链表结构
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章