Linux开发中内核通用链表的操作方法
我们在项目开发的时候要怎么封装双向链表库呢?下面就让我们带着这个疑问一起来学习这个结构,我们程序员只需要提供相关接口即可实现,有需要的朋友赶紧进入下文学习Linux开发中内核通用链表的操作方法吧!
传统的链表结构
struct node{
  int key;
  int val;
  node* prev;
  node* next;
 }
linux 内核通用链表库结构
提供给我们的指针域结构体:
struct list_head {
  struct list_head *next, *prev;
};
我们只需要包含它就可以:
struct node{
  int val;
  int key;
  struct list_head* head;
}
可以看到通过这个 list_head 结构就把我们的数据层跟驱动层分开了,而内核提供的各种操作方法接口也只关心 list_head 这个结构,也就是具体链接的时候也只链接这个list_head 结构,并不关心你数据层定义了什么类型.
一些接口宏定义
//初始化头指针
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
  struct list_head name = LIST_HEAD_INIT(name)
//遍历链表
#define __list_for_each(pos, head) \
  for (pos = (head)->next; pos != (head); pos = pos->next)
//获取节点首地址(不是list_head地址,是数据层节点首地址)
#define list_entry(ptr, type, member) \
  container_of(ptr, type, member)
//container_of在Linux内核中是一个常用的宏,用于从包含在某个
//结构中的指针获得结构本身的指针,通俗地讲就是通过结构体变
//量中某个成员的首地址进而获得整个结构体变量的首地址
#define container_of(ptr, type, member) ({     \
    const typeof( ((type *)0)->member ) *__mptr = (ptr);  \
    (type *)( (char *)__mptr - offsetof(type,member) );})
#define offsetof(s,m) (size_t)&(((s *)0)->m)
使用方式
typedef struct node{
  int val;
  int key;
  struct list_head* list;
}node;
//初始化头指针
LIST_HEAD(head);
//创建节点
node* a = malloc(sizeof(node));
node* b = malloc(sizeof(node));
//插入链表 方式一
list_add(&a->list,&head);
list_add(&b->list,&head);
//插入链表 方式二
list_add_tail(&a->list,&head);
list_add_tail(&b->list,&head);
//遍历链表  
struct list_head* p;
struct node* n;
__list_for_each(p,head){
  //返回list_head地址,然后再通过list_head地址反推
  //节点结构体首地址.
  n = list_entry(pos,struct node,list);
}
list_add 接口,先入后出原则,有点类似于栈

list_add-先入后出模式
list_add_tail 接口,先入先出原则,有点类似于fifo

list_add-先入先出模式
我们的链表节点,实际在内存中的展示形态

节点描述
可以看到最终的形态是,通过指向每个结构体里面的 list_head 类型指针,然后把它们串联起来的
list_entry 接口,通过结构体变量某个成员的地址,反推结构体首地址,就像 __list_for_each 接口只返回 list_head 地址,所以我们要通过这个成员地址在去获取它本身的结构体首地址,底层实现方法 container_of 宏

反推结构体首地址
举个例子
这个例子包括简单的增、删、遍历
#include#include #include #include #include MODULE_LICENSE("GPL"); MODULE_AUTHOR("David Xie"); MODULE_DESCRIPTION("List Module"); MODULE_ALIAS("List module"); struct student //代表一个实际节点的结构 { char name[100]; int num; struct list_head list; //内核链表里的节点结构 }; struct student *pstudent; struct student *tmp_student; struct list_head student_list; struct list_head *pos; int mylist_init(void) { int i = 0; //初始化一个链表,其实就是把student_list的prev和next指向自身 INIT_LIST_HEAD(&student_list); pstudent = kmalloc(sizeof(struct student)*5,GFP_KERNEL);//向内核申请5个student结构空间 memset(pstudent,0,sizeof(struct student)*5); //清空,这两个函数可以由kzalloc单独做到 for(i=0;istudent %d name: %s/n",tmp_student->num,tmp_student->name); } return 0; } void mylist_exit(void) { int i ; /* 实验:将for换成list_for_each来遍历删除结点,观察要发生的现象,并考虑解决办法 */ for(i=0;i 
上述就是爱站技术频道小编介绍的Linux开发中内核通用链表的操作方法,我们在网站建设的时候思维要活跃和广阔一些,也可以多多参考js.aizhan.com的文章。
 
                    