Linux内核链表

来源:互联网 发布:java源文件的扩展名 编辑:程序博客网 时间:2024/06/05 20:13

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.oKDIR := /home/unix/NO.3/2-Linux/linux-mini2440/all:make -C $(KDIR) M=$(PWD) modules CROSS_COMPILE=arm-linux- ARCH=arm




原创粉丝点击