内核双向链表的数据结构
他只有两个指针域,没有数据域,但是这样他的通用性得到了优化,但是这样没有数据,有什么意义呢,我们先来看看内核提供的接口函数
struct list_head
{
struct list_head *next,*head;
};
链表的创建函数,创建一个结点,并初始化
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
链表的插入的头插法
static inline void list_add(struct list_head *new, struct list_head *head)
{
__list_add(new, head, head->next);
}
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;
}
链表的尾插法
74 static inline void list_add_tail(struct list_head *new, struct list_head *head)
75 {
76 __list_add(new, head->prev, head);
77 }
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;
}
链表的删除
static inline void __list_del(struct list_head * prev, struct list_head * next)
{
next->prev = prev;
prev->next = next;
}
static inline void list_del(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
entry->next = LIST_POISON1; //指向两个无效的地址
entry->prev = LIST_POISON2;
}
链表的遍历
#define list_for_each(pos, head) \
for (pos = (head)->next; pos != (head); pos = pos->next)
写一个测试程序测试一下
#include <linux/init.h>
#include <linux/module.h>
#include <linux/list.h>
#include <linux/slab.h>
#define N 128
struct stu{
char name[100];
int id;
int math;
struct list_head list;
};
LIST_HEAD (head);
static int demo_init (void)
{
int i,ret;
struct stu *stu;
struct list_head *pos;
for (i=0;i<N;i++){
stu =kmalloc(sizeof(*stu),GFP_KERNEL);
if (NULL==stu) {
ret=-ENOMEM;
goto error0;
}
stu->id =9527+i;
sprintf (stu->name,"chouchou%d",i);
stu ->math = (60+i)%101;
list_add_tail(&stu->list,&head);
}
list_for_each(pos,&head) {
stu=container_of(pos,struct stu,list);
printk ("-------id %d -------\n",stu->id);
printk ("name %s , math: %d\n",stu->name,stu->math);
}
return ret;
error0:
return ret;
}
module_init (demo_init);
static void demo_exit (void)
{
struct list_head *pos,*n;
struct stu*stu;
list_for_each_safe(pos,n,&head)
{
stu =container_of(pos,struct stu,list);
kfree (stu);
}
}
module_exit (demo_exit);
container_of宏的理解
我们刚才在上面看双向循环链表结构的时候发现,他没有数据域,所以我们怎么知道他的数据,在上面的测试程序中我们使用了container_of这个宏来返回一个指针,其中就包含数据,所以我们来看一下container_of这个宏怎么得到数据
/**
677 * container_of - cast a member of a structure out to the containing structure
678 * @ptr: the pointer to the member.
679 * @type: the type of the container struct this is embedded in.
680 * @member: the name of the member within the struct.
681 *
682 */
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
683 #define container_of(ptr, type, member) ({ \
684 const typeof( ((type *)0)->member ) *__mptr = (ptr); \
685 (type *)( (char *)__mptr - offsetof(type,member) );})
来大概理解一下这个宏,他的核心思想就是通过知道你定义的类型指针,定义的类型,还有成员的名字来确定出包含这个链表的结构体的地址,从而获取数据域
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
//将ptr的类型指针赋值给通过tyoeof新类型的变量__mptr
(type *)( (char *)__mptr - offsetof(type,member) );})
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
//将0地址转换为指针并指向成员再通过取地址操作得到偏移量
//再用成员的地址减去偏移地址就得到结构体的首地址,再将他强转为目标结构体类型