linux内核链表大剖析

前言

链表是linux内核中最简单,最普通的数据结构,链表是一种存放和操作可变数量元素的数据结构,链表和静态数组的不同之处在于,它所包含的元素都是动态创建并插入链表的,在编译时不必知道具体需要创建多少个元素,链表元素在内存中无须占用连续内存区,在linux内核中父子兄弟进程之间的联系,platform总线上的设备,input输入子系统都是用链表组织起来的,可见linux内核链表是多么重要!
linux内核链表的位置及依赖
位置:
\include\linux\list.h
依赖:
#include <linux/types.h>
#include <linux/stddef.h>
#include <linux/poison.h>
#include <linux/prefetch.h>
特别注意
当我们把linux链表代码移值时记得清除平台相关的代码(GNU C)
1.({})
2. typeof
3. __builtin_prefetch
4. static inline

Linux内核链表的实现

linux内核链表是带头节点的双向循环链表,且头节点为表中成员,头结点的next指向首结点,头节点的prev指向尾结点
在这里插入图片描述
内核链表代码在头文件<linux/list.h>中声明,其数据结构很简单

struct list_head {
    struct list_head *next, *prev;
};
next指针指向下一个链表节点
pre指针指向前一个

读者就会发问了,那么数据放在哪里呢?其实在linux内核中,链表的方式与众不同,它不是将数据结构塞入链表,而是将链表节点塞入数据结构,简单来说数据放哪里由使用链表的人来自定义了,如果我们使用Linux内核链表,那么我们需要来自定义下链表节点,而且这个链表节点必须包含类型为struct list_head的成员如以下示例

struct Node
{
   struct list_head head;
   Type1 value;
   Type2 value2;
}

linux内核链表的创建及初始化

struct Node
{
   struct list_head head;//放在第一个成员的位置
   int value;//数据相关的成员
};
int main(void)
{
  struct Node l = {0};//创建及初始化
  struct list_head* list = (struct list_head*)&l;//是list指针指向的位置就是一个合法的struct list_head类型的变量,比如这里指向了l的head成员
  INIT_LIST_HEAD(list);//所以可以调用linux链表中的init函数初始化这个头结点
}

linux内核链表源码剖析

1.INIT_LIST_HEAD

static void INIT_LIST_HEAD(struct list_head *list)
{
    list->next = list;
    list->prev = list;
}

用来初始化链表头结点list的next和prev指针都指向自己,换句话说在初始化头结点的时候,让自己形成了一个双向循环的链表,
2.list_add(new,head)
在链表头部插入节点

static 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, head->next);
}

图解代码
在这里插入图片描述
可以看出这样就在链表头部插入一个结点

3.list_add_tail(new,head)
在链表尾部插入节点

static 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_tail(struct list_head *new, struct list_head *head)
{
    __list_add(new, head->prev, head);
}

图解代码,在这里插入图片描述
可以看出该代码就在链表尾部插入了一个新节点
4.list_del删除元素

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;//定义成空指针
}

图解代码
在这里插入图片描述
可以看出这样就把需要删除的节点从链表中删除了
5.正向遍历链表 list_for_each(pos,head)

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

/**

可以看出遍历的实现是比较简单的,从首节点逐一遍历到尾结点结束
6.逆向遍历链表 list_for_each(pos,head)

 */
#define list_for_each_prev(pos, head) \
    for (pos = (head)->prev; prefetch(pos->prev), pos != (head); \
            pos = pos->prev)

/**

可以看出逆向遍历的实现也是比较简单的,从尾结点逐一遍历到首节点结束

简单使用下linux内核链表

#include <stdio.h>
#include "LinuxList.h"

void list_demo_1()
{
    struct Node
    {
        struct list_head head;
        int value;
    };

    struct Node l = {0};
    struct list_head* list = (struct list_head*)&l;
    struct list_head* slider = NULL;
    int i = 0;

    INIT_LIST_HEAD(list);

    printf("Insert begin ...\n");

    for(i=0; i<5; i++)
    {
        struct Node* n = (struct Node*)malloc(sizeof(struct Node));

        n->value = i;

        list_add_tail((struct list_head*)n, list);
    }

    list_for_each(slider, list)
    {
        printf("%d\n", ((struct Node*)slider)->value);
    }

    printf("Insert end ...\n");

    printf("Delete begin ...\n");

    list_for_each(slider, list)
    {
        if( ((struct Node*)slider)->value == 3 )
        {
            list_del(slider);
            free(slider);
            break;
        }
    }

    list_for_each(slider, list)
    {
        printf("%d\n", ((struct Node*)slider)->value);
    }

    printf("Delete end ...\n");
}

void list_demo_2()
{
    struct Node
    {
        int value;
        struct list_head head;
    };

    struct Node l = {0};
    struct list_head* list = &l.head;
    struct list_head* slider = NULL;
    int i = 0;

    INIT_LIST_HEAD(list);

    printf("Insert begin ...\n");

    for(i=0; i<5; i++)
    {
        struct Node* n = (struct Node*)malloc(sizeof(struct Node));

        n->value = i;

        list_add(&n->head, list);
    }

    list_for_each(slider, list)
    {
        printf("%d\n", list_entry(slider, struct Node, head)->value);
        /*
         #define list_entry(ptr, type, member) \
         container_of(ptr, type, member)
       */
    }

    printf("Insert end ...\n");


    printf("Delete begin ...\n");

    list_for_each(slider, list)
    {
        struct Node* n = list_entry(slider, struct Node, head);

        if( n->value == 3 )
        {
            list_del(slider);
            free(n);
            break;
        }
    }

    list_for_each(slider, list)
    {
        printf("%d\n", list_entry(slider, struct Node, head)->value);
    }

    printf("Delete end ...\n");
}

int main()
{
    // list_demo_1();
    list_demo_2();

    return 0;
}
注意demo1和demo2的Node节点中的struct list_head成员的顺序不同,采用的语法也不同

结果为:

4
3
2
1
0

4
2
1
0

在这里插入图片描述
额外补充:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

发布了83 篇原创文章 · 获赞 3 · 访问量 1230

猜你喜欢

转载自blog.csdn.net/qq_41936794/article/details/105594619