(本节笔记的实验代码,在这里)
1.普通链表与Linux内核链表的对比
链表是一种常用的数据结构,它通过指针将一系列数据节点连接成一条数据链。相对于数组,链表具有更好的动态性,建立链表时无需预先知道数据总量,可以随机分配空间,可以高效地在链表中的任意位置实时插入或删除数据。链表的开销主要是访问的顺序性和组织链的空间损失。链表分为单向节点、双向链表、循环链表和双向循环链表,Linux内核链表属于双向循环链表。
内核链表的指针是指向下一节点的指针域,并不是指向下一节点的数据域,需要访问数据时,再通过函数访问数据域,以实现建立不同数据格式都能统一操作的链表。
2.内核链表的使用
2.1创建链表
2.1.1函数名:
INIT_LIST_HEAD
2.1.2函数原型:
static inline void INIT_LIST_HEAD(struct list_head *list);
2.1.3参数说明:
*list:struct list_head结构的指针,也就是被初始化的链表头。
2.2在链表头插入节点
2.2.1函数名:
list_add
2.2.2函数原型:
2.2.3参数说明:
2.3在链表尾插入节点
2.3.1函数名:
list_add_tail
2.3.2函数原型:
static inline void list_add_tail(struct list_head *new,struct list_head *head);
2.3.3参数说明:
*new:需要插入的节点的指针域地址。
*head:被插入的链表头地址。
2.4删除节点
2.4.1函数名:
list_del
2.4.2函数原型:
static inline void list_del(struct list_head *entry);
2.4.3参数说明:
*entry:要删除的节点的指针域的指针。
2.5取出节点
2.5.1函数名:
list_entry
2.5.2函数原型:
list_entry(ptr, type, member)
2.5.3参数说明:
ptr:指向节点指针域的指针。
type:节点内容的类型。
member:节点指针域的成员名。
2.6遍历链表
2.6.1函数名:
list_for_each
2.6.2函数原型:
list_for_each(pos,head)
//{
// for(pos = (head)->next; pos != (head); pos = pos->next)
// tmp = list_entry(pos,struct score, list)
//}
//返回的tmp是指向struct score结构的指针。
2.6.3参数说明:
pos:被遍历的每个节点的指针域的指针。
head:要被遍历的链表头指针。
学习内核函数,打开Sourceinsight参考内核代码本身!所有内核链表都保存在list.h中。
范例代码 touch mylist.c
『
#include <linux/init.h>
#include <linux/module.h>
struct score
{
int num;
int english;
int math;
struct list_head list;
};
struct list_head scorce_head;
struct score stu1, stu2, stu3;
struct list_head *pos;
struct score *tmp;
static int mylist_init()
{
INIT_LIST_HEAD(&score_head); //初始化链表头
stu1.num = 1;
stu1.english = 90;
stu1.math = 98;
list_ade_tail(&(stu1.list), &scoure_head); //在链表尾插入节点
stu2.num = 2;
stu2.english = 92;
stu2.math = 91;
list_ade_tail(&(stu2.list), &scoure_head); //在链表尾插入节点
stu3.num = 3;
stu3.english = 94;
stu3.math = 95;
list_ade_tail(&(stu3.list), &scoure_head); //在链表尾插入节点
list_for_each(pos, &score_head)
{
tmp = lista_entry(pos, struct_score, list);
printk("NO. is %d, english is %d, math is %d\n",tmp->num, tmp->english, tmp->math);
}
return 0;
}
static void mylist_exit()
{
list_del(&(stu1.list));
list_del(&(stu2.list));
}
module_init(mylist_init);
module_exit(mylist_exit);
』
touch Makefile(注意PWD为大写)
『
obj-m := mylist.o
KDIR := /arm/linux 2.6.30.4
all:
make -C $(KDIR) M=$(PWD) modules ARCH=arm CROSS_COMPILE=arm-linux-
clean:
rm -f *.o *.ko *.order *.symvers
』
3.内核链表实现分析
3.1初始化链表头
让它的前向指针和后向指针都指向它自己。
3.2插入节点
指针域的前向指针和后向指针的内容更改。
3.3取出节点
通过member计算出节点的内容的偏移,通过偏移值来找到节点内容的地址,从而取出节点的内容。
4.移植内核链表
直接复制内核源码中关于内核链表的代码到程序当中。