内核list结构的用法

内核中处理链表数据时,会用list_head数据类型,以及对其的一系列操作,list_head是一种双向链表,它只包含两个指针,分别指向前后两个节点。在实际使用时,你可以定义自己所需的结构类型,然后将list_head包含其中,其他是有用数据,这样的话list_head负责联系前后节点形成双向链表,数据则绑在list_head上,使问题得以分解,见者容易理解,套用更加方便。

第一部分

先看一看list.h中的各部分:


/*list_head 结构类型*/
struct list_head {
        struct list_head *next, *prev;
};

包含两个指针,指向前后两个节点,说明是双向链表


/*初始化一个节点,前后指针均指向自己*/
static inline void INIT_LIST_HEAD(struct list_head *list)
{
        list->next = list;
        list->prev = list;
}

/*向链表add一个节点,在prev和next之间插入一个new*/
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;
}

添加节点时,可以通过传入不同头结点前后指针,可以在尾部和首部添加,假设头节点next方向为正方向,比如下面这个:

static inline void list_add(struct list_head *new, struct list_head *head)
{
        __list_add(new, head->prev, head);
}

就是从尾部添加,如果是__list_add(new, head, head->next)就是在首部添加。


/*删除一个节点*/
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 = NULL;
        entry->prev = NULL;
}

/*list_entry,从list成员得到整体结构体地址*/
#define list_entry(ptr, type, member)  container_of(ptr, type, member)
#define offsetoflist(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#define container_of(ptr, type, member) ({                      \
        const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
        (type *)( (char *)__mptr - offsetoflist(type,member) );})

第一个宏太简单,container_of是内核常用的套路,从成员地址获取整体结构体的地址
第二宏offsetoflist,TYPE可以认为是一个结构体类型,MEMBER是其中的成员名,将0强制变成TYPE可认为结构体起始位置为0,然后&((TYPE)0)->MEMBER,取其成员的地址并转为size_t,就是我们的结果值,其实就是成员所在地址相对于结构体起始地址的偏移量。
第三个宏container_of,它的作用是已知结构体中一成员名和其地址,求得整个结构体的地址。ptr为成员地址,type为结构体类型,member为结构体成员名。typeof(((type )0)->member)这表示其实就是成员的类型,所以第一句ptr赋给__mptr的const中间变量,这样做的目的更安全,const修饰表示ptr不被修改,中间变量防止ptr的多次出现,当ptr被赋值成p++时出错。第二句是用成员地址减去它相对于结构体地址的偏移量,自然是结构体的地址。


/*遍历list的for循环*/
#define list_for_each(pos, head) \
        for (pos = (head)->next; pos != (head);\
                pos = pos->next)
#define list_for_each_safe(pos, n, head) \
        for(pos = (head)->next,n=pos->next; pos!=(head);\
                pos = n,n = pos->next)

这里有两个宏,都是一个for循环,pos代表位置,head是头结点,两者的唯一区别在于,list_for_each_safe多了一个中间变量n,这么做的原因是,如果你在遍历到特定节点进行删除操作时,那么第一个的pos->next就找不到啦,而第二个刚开始就把pos->next存给了n,无论你对pos如何操作,都可以找到。


/*判断list是否为空*/
static inline int list_empty(const struct list_head *head)
{
        return head->next == head;
}

简单不解释


下面是list.h:

#ifndef _LINUX_LIST_H
#define _LINUX_LIST_H
#include 
 
struct list_head {
         struct list_head *next, *prev;
};


static inline void INIT_LIST_HEAD(struct list_head *list)
{
        list->next = list;
        list->prev = 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;
}


static inline void list_add(struct list_head *new, struct list_head *head)
{
        __list_add(new, head->prev, head);
}
 
 
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 = NULL;
        entry->prev = NULL;
}



#define list_entry(ptr, type, member) \
         container_of(ptr, type, member)

#define container_of(ptr, type, member) ({                      \
        const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
        (type *)( (char *)__mptr - offsetoflist(type,member) );})

#define offsetoflist(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

#define list_for_each(pos, head) \
        for (pos = (head)->next;pos != (head);\
                pos = pos->next)


#define list_for_each_safe(pos, n, head) \
        for(pos = (head)->next,n=pos->next; pos!=(head);\
                pos = n,n = pos->next)


static inline int list_empty(const struct list_head *head)
{
        return head->next == head;
}

#endif

第二部分

使用简单例子测试list.h

#include "list.h"
#include 
#include 
#include 

//包含list结构的有用结构体类型
struct person{
    struct list_head list;
    int age;
    char name[50];
};

int main(int argc, char const *argv[])
{
        //定义两个结构体变量并赋值
    struct person Jack;
    struct person Tom;
    Jack.age = 11;
    strcpy(Jack.name,"Jack");
    Tom.age = 15;
    strcpy(Tom.name,"Tom");

    //定义头结点并初始化
    struct list_head head;
    INIT_LIST_HEAD(&head);
        
    //把两个结构体变量中list节点加入到链表
    list_add(&Jack.list,&head);
    list_add(&Tom.list,&head);

    //遍历链表,然后通过链表指针获取整体结构体指针,打印
    struct list_head * pList;
    struct person * pPerson;
    printf("------1--------\n");
    list_for_each(pList,&head){
        pPerson = list_entry(pList,struct person,list);
        printf("age:%d\nname:%s\n\n",pPerson->age,pPerson->name);
    }

    //加入申请内存的新节点
    struct person *pMary = (struct person *)malloc(sizeof(struct person));
    pMary->age = 16;
    strcpy(pMary->name,"Mary");
    list_add(&pMary->list,&head);
    printf("------2--------\n");
    list_for_each(pList,&head){
        pPerson = list_entry(pList,struct person,list);
        printf("age:%d\nname:%s\n\n",pPerson->age,pPerson->name);
    }


   //遍历链表,使用带safe的,找到特定节点进行删除,释放申请内存,这里删除后break的话,可以不带safe,如果还要遍历,带上
    struct list_head * templist;
    list_for_each_safe(pList,templist,&head){
        pPerson = list_entry(pList,struct person,list);
        if( strcmp(pPerson->name,"Mary") == 0){
            list_del(&pPerson->list);
            free(pPerson);
        }
    }
    //遍历
    printf("------3--------\n");
    list_for_each(pList,&head){
        pPerson = list_entry(pList,struct person,list);
        printf("age:%d\nname:%s\n\n",pPerson->age,pPerson->name);
    }   
    return 0;
}

打印结果:


------1--------
age:11
name:Jack

age:15
name:Tom

------2--------
age:11
name:Jack

age:15
name:Tom

age:16
name:Mary


------3--------
age:11
name:Jack

age:15
name:Tom

你可能感兴趣的:(内核list结构的用法)