Linux内核链表

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也指向自己,就是一个空的双向循环列表。


 list_add:在链表头插入节点
/*
 * 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:取出节点
/**
 * 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);

Makefile:
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




  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值