内核链表的简单使用

前言

(一)首先介绍一下内核链表:

1、内核链表常出现在系统内部,是ubuntu内部提供的一种双向循环链表 。

        双向循环链表:所有的东西都是自定义的:数据域,prev和next指针

         内核链表:只有数据域是我们自定义:其他的操作在内核链表文件list.h中均有定义声明

2、内核链表与普通链表的区别:
        1. 普通链表当中数据域和指针域,没有做到区分。数据与指针形成了一个整体,而内核链表数据与指针是完全剥离的没有直接的关系。
        2. 在普通链表当中所有节点的数据都是一样的类型,而内核链表中每一个节点的类型都可以是不同的。
        3. 普通的链表每一个节点都只能出现在一个链表当中,因为它只有一对前驱/后继指针,而内核链表可以有多对前驱/后继指针。

(二)内核链表的好处:

        常见的单向链表和双向链表指针指向的是链表节点起始位置,在Linux内核中实际使用中有一些局限性,如数据区必须是固定的,而实际需求是多种多样的。这种方法无法构建一套通用的链表,因为每个不同的数据区需要一套链表。为此,Linux内核把所有链表操作方法的共同部分提取出来,把不同的部分留给代码编写者自己去处理。

        Linux内核实现了一套纯链表的封装,链表节点数据结构只有指针区而没有数据区,另外还封装了各种操作函数,如创建节点函数、插入节点函数、删除节点函数、遍历节点函数等。
 

(三)链表的初始化

链表的构成:大结构体与小结构体

大结构体: 包含了数据以及指针(内核链表中的小的结构体)
小结构体: 内核链表中用来指向前一个节点和后一个节点的指针
注意:
        在大结构体当中小结构体必须是一个普通变量不可以使用指针。
typedef struct kernel{//大结构体类型
        int data;//数据域
        struct list_head list;//小结构体名称 
}kernel_t,*pkernel_t;
list.h文件中list_head介绍如下        
struct list_head{
	struct list_head *next,*prev;
};

(四)链表增删改移动

1、插入操作

思路:

内核链表中插入数据的思路:
把用户传进来的两个参数: 新节点 + 头节点
演变成很三个参数: 新节点 + 新节点的前驱节点 + 新节点的后继节点

1 /**
2 * 节点头插
3 *
4 * 使用内联函数list_add(struct list_head *new, struct list_head *head)         
5 * 将会自动调用__list_add,实现功能:将节点 new 插入到 head 后面;实现头插功能
6 */
7 static inline void list_add(struct list_head *xnew, struct list_head *head)
8 {
9         __list_add(new, head, head->next);
10 }

头插法:

//头插操作 
void kernel_insert_head(pkernel_t p,int d)
{
    //根据数据域创建节点,并头插
    pkernel_t node=(pkernel_t)malloc(sizeof(kernel_t));
    if(NULL==node){
        perror("malloc");//当创建失败,根据错误码,显示原因
        return;//
    }    
    //根据传入的参,对节点数据域赋值 
    node->data=d;
    
    //将node插在p的后面 
    list_add(&node->list,&p->list);
     
}

list_add(&node->list,&p->list);

#ifndef CONFIG_DEBUG_LIST
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;
}
#else
extern void __list_add(struct list_head *new,
			      struct list_head *prev,
			      struct list_head *next);
#endif

/**
 * 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);
}
尾插法:
void kernel_insert_tail(pkernel_t p,int d)
{
    //根据数据域创建节点,并尾插
    pkernel_t node=(pkernel_t)malloc(sizeof(kernel_t));
    if(NULL==node){
        return;
    }
    
    node->data=d;//根据数据域赋值 
    
    list_add_tail(&node->list,&p->list);      
}

list.h中:

/**
 * 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);
}
2、删除操作
//删除操作
void kernel_del(pkernel_t p,int d)
{
    //1>查找数据域d是否存在
    pkernel_t pos;  //定义的循环变量
    list_for_each_entry(pos,&p->list,list)
    {
        if(pos->data==d){
            list_del(&pos->list);
            free(pos);
            return;
        }
        
    }
    printf("没有找到该数据\n");   
}

for循环函数:

/**
 * list_for_each_entry	-	iterate over list of given type
 * @pos:	the type * to use as a loop cursor.
 * @head:	the head for your list.
 * @member:	the name of the list_struct within the struct.
 */
#define list_for_each_entry(pos, head, member)				\
	for (pos = list_entry((head)->next, typeof(*pos), member);	\
	     &pos->member != (head); 	\
	     pos = list_entry(pos->member.next, typeof(*pos), member))

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.
 */
#ifndef CONFIG_DEBUG_LIST
static inline void __list_del_entry(struct list_head *entry)
{
	__list_del(entry->prev, entry->next);
}

static inline void list_del(struct list_head *entry)
{
	__list_del(entry->prev, entry->next);
	entry->next = LIST_POISON1;
	entry->prev = LIST_POISON2;
}
#else
extern void __list_del_entry(struct list_head *entry);
extern void list_del(struct list_head *entry);
#endif
3、更新操作
//替换操作
void kernel_update(pkernel_t p,int old,int new)
{
    //1>查找一下旧数据是否在链表当中
    pkernel_t pos;
    pkernel_t temp;//传输替换的数据结构体
    list_for_each_entry(pos,&p->list,list)
    {
        if(pos->data==old){
                temp=(pkernel_t)malloc(sizeof(kernel_t));
                temp->data=new;
                list_replace_init(&pos->list,&temp->list);
                free(pos); 
                return;
        }

    }
    printf("没有找到该数据\n");
}

   替换:

static inline void list_replace(struct list_head *old,
				struct list_head *new)
{
	new->next = old->next;
	new->next->prev = new;
	new->prev = old->prev;
	new->prev->next = new;
}

static inline void list_replace_init(struct list_head *old,
					struct list_head *new)
{
	list_replace(old, new);
	INIT_LIST_HEAD(old);
}
static inline void INIT_LIST_HEAD(struct list_head *list)
{
	list->next = list;
	list->prev = list;
}

(五)链表的遍历

内核链表由于没有数据域,数据域是我们自己操作的定义的,所以需要先知道数据域的大小,才可以通过小结构体与数据域差值得到大结构体的地址。
                              

分析如下:

  宏: #define list_entry(ptr, type, member) \
                container_of(ptr, type, member)
                
            #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
            #define container_of(ptr, type, member) ({			\
                const typeof( ((type *)0)->member ) *__mptr = (ptr);	\
                (type *)( (char *)__mptr - offsetof(type,member) );})
            解释: ptr:小结构体的指针类型:struct list_head *
                  type: 大结构体的类型:struct kernel ·
                  member:小结构体在大结构体中的名称:list
                  
            1>分析 
               list_entry(ptr, type, member)= container_of(ptr, type, member)
                
            2>container_of(ptr, type, member)=
                 const typeof( ((type *)0)->member ) *__mptr = (ptr);	\
                (type *)( (char *)__mptr - offsetof(type,member) );}) 

            3>offsetof(TYPE, member) = ((size_t) &((TYPE *)0)->member)


            4>综上所述,我们调用:list_entry(ptr, type, member)就会获得下面的内容
                const typeof( ((type *)0)->member ) *__mptr = (ptr);	\
                (type *)( (char *)__mptr - ((size_t) &((type *)0)->member)
                
            重点:宏分析--->是你掌握内核链表的重要一环
                1> const typeof( ((type *)0)->member ) *__mptr = (ptr);
                ((type *)0)--->将0强转为 大结构体的指针类型 0x0
                ((type *)0)->member 大结构体下的member= list 
                    typeof--->取类型
                  const struct list_head  * __mptr=ptr;
                    定义一个小结构体指针类型变量,保存小结构体的地址
                    
                2>(type *)( (char *)__mptr - ((size_t) &((type *)0)->member)
                    ((size_t) &((type *)0)->member)
                    (size_t) &list
                   -  无符号整型   ,将list的地址转化为无符号整型
                     
                   (type *)( (char *)__mptr 
                            字符类型 --->一次只能运算一个字符
                     (type *)大结构体类型  --->mptr转化为 char * 运算后,转化为大结构体类型
                        减去小结构体地址  就会得到 大结构体地址
   移动一个字节:偏移量:1                     
                总结: 
                     list_entry(ptr, type, member);
                              通过小结构体指针类型ptr  得到 大结构体指针类型
                              
                使用实例: 
                    struct list_head *ptr=随便一个小结构体地址;外部传入
                    pkernel_t node=list_entry(ptr,kernel,list);
                    printf("%d\n",node->data);

遍历函数:

//遍历
void display(pkernel_t p)
{
	
	if( p->list.next == &p->list )
 {
	printf("该表为空!!\n");
	return -1 ;
 }
    printf("遍历结果为:");	
    pkernel_t node;
    struct list_head *pos;
    list_for_each(pos,&p->list){//循环 
        node=list_entry(pos,kernel_t,list);
        printf("%d ",node->data);                    
    }
    printf("\n"); 
}

猜你喜欢

转载自blog.csdn.net/apple_71040140/article/details/132175057