🔥博客主页:PannLZ
🎋系列专栏:《Linux系统之路》
🥊不要让自己再留有遗憾,加油吧!
文章目录
- 链表
- 1.创建和初始化
- 2.创建节点
- 3.添加节点
- 4.删除节点
- 5.遍历
链表
内核开发者只实现了循环双链表,因为这个结构能够实现FIFO和LIFO,并且内核开发者要保持最少代码。为了支持链表,代码中要添加的头文件是<linux/list.h>
。
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
的功能可能可以阅读我关于它的笔记。