1、Linux内核链表介绍
对应链表大家应该都不陌生,我们来复习一下
1.链表简介
链表是一种常用的数据结构,它通过指针将一系列数据节点连接成一条数据链。相对于数组,链表具有更好的动态性,建立链表时无需预先知道数据总量,可以随机分配空间,可以高效地在链表中的任意位置实时插入或删除数据。链表的开销主要是访问的顺序性和组织链的空间损失。2.传统链表和内核链表对比
传统链表:一般指的是单向链表struct List
{
struct list *next;//链表结点指针域
};
内核链表:双向循环链表 设计初衷是设计出一个通用统一的双向链表!
struct list_head
{
struct list_head *head, *prev;
};
list_head结构包含两个指向list_head结构体的指针
prev和next,由此可见,内核的链表具备双链表功能,实际上,通常它都组织成双向循环链表。
3.内核链表-函数
1. INIT_LIST_HEAD:创建链表2. list_add:在链表头插入节点
3. list_add_tail:在链表尾插入节点
4. list_del:删除节点
5. list_entry:取出节点
6. list_for_each:遍历链表
那么这些函数改怎么用呢?最好的方法就是查看Linux内核源代码了。
INIT_LIST_HEAD创建链表
#define INIT_LIST_HEAD(ptr) do { \
(ptr)->next = (ptr); (ptr)->prev = (ptr); \
} while (0)
ptr是一个listhead类型的指针,将prev指向自己,将next也指向自己,就是一个空的双向循环列表。
/*
* Insert a new entry between two known consecutive entries.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
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 - add a new entry
* @new: new entry to be added
* @head: list head to add it after
*
* Insert a new entry after the specified head.
* This is good for implementing stacks.
*/
static __inline__ void list_add(struct list_head *new, struct list_head *head)
{
__list_add(new, head, head->next);
}
list_add声明为了一个内联函数,参数有2个,需要插入节点的指针和链表的头结点,它调用了__list_add函数,在这个函数里面有3个参数,用来完成链表的插入。第一步,头结点下一个节点前驱指向新节点,第二步,新节点的后继指向头结点的下一个节点,第三步,新节点的前驱指向头结点,第四步,头结点的后继指向新节点。这样就在链表的头部插入的新节点。
list_add_tail:在链表尾插入节点
/**
* list_add_tail - add a new entry
* @new: new entry to be added
* @head: list head to add it before
*
* Insert a new entry before the specified head.
* This is useful for implementing queues.
*/
static __inline__ void list_add_tail(struct list_head *new,
struct list_head *head)
{
__list_add(new, head->prev, head);
}
在尾部插入节点的方法实现的很巧妙,仅仅调用__list_add函数就实现了,因为他是循环链表,头结点的前一个节点即为尾节点,通过对头结点和尾节点指针的修改就可以把新节点插入到链表尾部,这里的步骤和上面的一样。
list_del:删除节点
/*
* Delete a list entry by making the prev/next entries
* point to each other.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
static __inline__ void __list_del(struct list_head *prev,
struct list_head *next)
{
next->prev = prev;
prev->next = next;
}
/**
* list_del - deletes entry from list.
* @entry: the element to delete from the list.
* Note: list_empty on entry does not return true after this, the entry is
* in an undefined state.
*/
static __inline__ void list_del(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
}
删除一个节点需要的参数是这个节点的指针‘,然后调用__list_del函数,将这个节点前驱的后继指向这个节点的后继,这个节点的后继的前驱指向这个节点的前驱,即实现了对这个节点的删除。
/**
* 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)
这个函数有3个参数,ptr是list_head指针,type是结构体的信息,因为根据list_head的信息无法得到结构体里面的数据信息,member是结构体里面的list_struct结构体的名字。
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; prefetch(pos->next), pos != (head); \
pos = pos->next)
这个函数有2个参数,pos是一个游标,是list_head结构体的信息,head是链表的头结点。
4、实例代码
list.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/list.h>
struct score
{
int no;
int math;
int english;
struct list_head list;
};
struct list_head score_head;
struct score stu1,stu2,stu3,stu4;
struct list_head *poc;
struct score *tmp;
MODULE_LICENSE("GPL");
static int list_init(void)
{
INIT_LIST_HEAD(&score_head);
stu1.no=1;
stu1.math=90;
stu1.english=88;
list_add(&(stu1.list),&score_head);
stu2.no=2;
stu2.math=100;
stu2.english=90;
list_add(&(stu2.list),&score_head);
stu3.no=3;
stu3.math=80;
stu3.english=90;
list_add(&(stu3.list),&score_head);
stu4.no=4;
stu4.math=95;
stu4.english=100;
list_add(&(stu4.list),&score_head);
list_for_each(poc,&score_head)
{
tmp = list_entry(poc, struct score, list);
printk("stu%d math:%d english:%d\n",tmp->no,tmp->math,tmp->english);
}
return 0;
}
static void list_exit(void)
{
list_del(&(stu1.list));
list_del(&(stu2.list));
}
module_init(list_init);
module_exit(list_exit);
obj-m := list.o
KDIR := /home/unix/NO.3/2-Linux/linux-mini2440/
all:
make -C $(KDIR) M=$(PWD) modules CROSS_COMPILE=arm-linux- ARCH=arm