struct list_head {
struct list_head *next, *prev;
};
从结构上看,就是一个简单的双向链表。在<linux/list.h>中定义其相关的操作。
主要API简介:
函数 | 作用 |
INIT_LIST_HEAD() | 初始化表头 |
list_add | 在表头后增加一个元素 |
list_add_tail | 在链表尾部增加一个元素 |
list_del | 从链表中删除一个元素 |
list_replace | 用另一个元素替代链表中的某个元素 |
list_entry | get the struct for this entry |
list_for_each_entry | iterate over list of given type |
list_for_each_prev_safe | iterate over a list backwards safe against removal of list entry |
list_empty | tests whether a list is empty |
list_splice | join two lists, this is designed for stacks |
list_head结构的用法是,将其嵌入其他数据结构中。同一个数据结构可以嵌入多个链表头,这样该数据结构就可以被连接到多个链表中,每个list_head用于其中的一个链表。
将list_head结构嵌入其他结构的用法类似面向对象中继承或者实现接口,c中没有继承和实现接口的操作,故用此种方法实现。
双向链表的头通常是一个独立的list_head结构。
在使用之前,必须用INIT_LIST_HEAD宏来初始化链表头。可如下声明并初始化一个实际的链表头:
struct todo_struct {
struct list_head list;
int priority;
/* ... */
}
struct list_head todo_list;
INIT_LIST_HEAD(&todo_list);
另外,也可在编译时像下面这样初始化链表:
LIST_HEAD(&todo_list);
宏的实际内如下:
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
list_head结构体有利于实现具有类似结构的链表,但调用程序通常对组成链表的大结构更感兴趣。因此,可利用上面提到的API list_entry宏将一个list_head结构指针映射回指向包含它的大结构指针。
/**
* list_entry - get the struct for this entry
* @ptr: the &struct list_head pointer.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_struct within the struct.
*/
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
这里可以看到,list_entry内部的实现是调用了我们之前提到的container_of宏。
对于链表这个东西当然是少不了遍历操作的。例如上面的结构体do_struct,如果在新元素插入时,需要按照优先级降序排列,这增加新元素的的函数可如下实现:
void todo_add_entry(struct todo_struct *new) {
struct list_head *ptr;
struct todo_struct *entry;
/* todo_list是之前初始化的链表头 */
for (ptr = todo_list.next; ptr != &todo_list; ptr = ptr->next) {
entry = list_entry(ptr, struct todo_struct, list);
if (entry->priority < new-> priority) {
list_add_tail(&new->list, ptr);
return;
}
}
list_add_tail(&new->list, &todo_list);
}
BUT,在双向链表的API中,已经提供了一个遍历链表的宏list_for_each
/**
* list_for_each - iterate over a list
* @pos: the &struct list_head to use as a loop cursor.
* @head: the head for your list.
*/
#define list_for_each(pos, head) \
for (pos = (head)->next; pos != (head); pos = pos->next)
故,上方的代码可改写如下:
void todo_add_entry(struct todo_struct *new) {
struct list_head *ptr;
struct todo_struct *entry;
/* todo_list是之前初始化的链表头 */
list_for_each(ptr, &todo_list) {
entry = list_entry(ptr, struct todo_struct, list);
if (entry->priority < new-> priority) {
list_add_tail(&new->list, ptr);
return;
}
}
list_add_tail(&new->list, &todo_list);
}
当然API还包括有反向遍历和带safe后缀的遍历,其中,如果循环可能会删除链表中的项,就应该使用带safe后缀的版本遍历。
系统还提供了一些直接遍历entry的宏,如:
/**
* list_for_each_entry - iterate over list of given type
* @pos: the type * to use as a loop cursor.
* @head: the head for your list.
* @member: the name of the list_struct within the struct.
*/
#define list_for_each_entry(pos, head, member) \
for (pos = list_first_entry(head, typeof(*pos), member); \
&pos->member != (head); \
pos = list_next_entry(pos, member))
使用这些宏的话,连在循环里面调用list_entry都可以省了。