【Linux】内核中的链表

在这里插入图片描述

博客主页:PannLZ
系列专栏:《Linux系统之路》
不要让自己再留有遗憾,加油吧!


文章目录

    • 链表
      • 1.创建和初始化
      • 2.创建节点
      • 3.添加节点
      • 4.删除节点
      • 5.遍历


链表

内核开发者只实现了循环双链表,因为这个结构能够实现FIFO和LIFO,并且内核开发者要保持最少代码。为了支持链表,代码中要添加的头文件是

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

Struct list_head用在链表头和每个节点中。在内核中,将数据结构表示为链表之前,该结构必须嵌入struct list_head字段。

struct car {
		int door_number;
		char *color;
		char *model;
		struct list_head list; /*内核的表结构 */
};

1.创建和初始化

1).动态方法

动态方法由struct list_head组成,用INIT_LIST_HEAD宏初始化:

struct list_head mylist;
INIT_LIST_HEAD(&mylist);

static inline void INIT_LIST_HEAD(struct list_head *list)
{
		list->next = list;
		list->prev = list;
}

2).静态方法

静态分配通过LIST_HEAD宏完成:

LIST_HEAD(mylist)
 
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
    
#define LIST_HEAD_INIT(name) { &(name), &(name)}
//这为name字段内的每个指针(prev和next)赋值,使其指向name自身(就像INIT_LIST_HEAD做的那样)。

补充:

关于宏中的“\”:\ 的部分,这是 C 语言中的行连接符,也被称为续行符。如果宏的定义超过一行,我们可以使用 \ 来表示该宏的定义还未结束,下一行仍然是该宏的一部分。

2.创建节点

要创建新节点,只需创建数据结构实例,初始化嵌入在其中的list_head字段。以汽车为例,其代码如下:\

struct car *blackcar = kzalloc(sizeof(struct car), GFP_KERNEL);
/* 非静态初始化,因为它是嵌入的列表字段*/
INIT_LIST_HEAD(&blackcar->list);

3.添加节点

内核提供的list_add用于向链表添加新项,它是内部函数__list_add的包装:

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

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(&redcar->list, &carlist);
list_add(&blue->list, &carlist);

void list_add_tail(struct list_head *new, structlist_head *head);//尾插

//添加两辆车,类似队列
list_add_tail(&redcar->list, &carlist);
list_add_tail(&blue->list, &carlist);

4.删除节点

void list_del(struct list_head *entry);

//删除红色车
list_del(&redcar->list);

list_del断开指定节点的prev和next指针,移除该节点。分配给该节点的内存需要使用kfree手动释放。

5.遍历

使用宏list_for_each_entry(pos, head, member)进行链表遍历。

  • head:链表的头节点head可以是链表的头节点或任意一项,这没关系,因为所处理的是双向链表
  • member:数据结构(在我们的例子中,它是list)中链表struct list_head的名称。
  • pos:用于迭代。
struct car *acar; /* 循环计数器,用于迭代*/
int blue_car_num = 0;
/* list是数据结构中的list_head结构的名称 */
list_for_each_entry(acar, carlist, list)
{
		if(acar->color == "blue")
		blue_car_num++;
}

为什么list_for_each_entry需要传入链表struct list_head的名称呢?请看下面

#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))

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

鉴于此,我们可以理解这都是container_of的功能。
关于container_of的功能可能可以阅读我关于它的笔记。

你可能感兴趣的:(Linux系统之路,linux,驱动开发,链表,c语言)