LINUX内核链表的详细解析

一、常规链表的缺陷:
第一:每一种节点都是特殊的,导致每一条链表都是特殊的,因此 每一种链表的增删查改也都是特殊的。注意:特殊的不具备有通用性
第二:当每一种节点处于变化的数据结构网络中时,节点指针无法指向稳定不变的节点。

比如:有一千多个节点,那么用常规的链表来插入就需要一千种插入函数。

二、常规链表和内核链表的区别:
1.一个包含数据域(常规链表),一个不包含数据域(内核链表)。
三、内核链表
内核链表的原理:
1.将链表结构抽象出来:去除节点中的具体数字,只保留表示逻辑的双向指针。形成一条只包含逻辑的“纯粹的链表”。
2.将此链表“寄宿”于具体数据的节点之中,使之贯穿于这些节点。
3.统一内核链表的操作接口
①初始化链表:INIT_LIST_HEAD()/LIST_HEAD_INIT()
②插入节点:list_add()/list_add_tail()
③删除节点:list_del()/list_del_init()
④移动节点:list_move()/list_move_tail()
⑤合并链表:list_splice()
⑥后向遍历:list_for_each()/list__for_each_safe()/list__for_each_entry()
⑦前向遍历:list_for_each_prev()/list__for_each_entry_prev()
⑧判断是否为空:list_empty()
⑨取得宿主节点指针:list_entry()
4.图形加深认识
LINUX内核链表的详细解析_第1张图片LINUX内核链表的详细解析_第2张图片
5.下面通过内核链表的源码做一项练习:奇偶分离。
项目:

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

//一个具体的“大结构体”
struct node
{
    int data;

    //只包含链表逻辑的“小结构体”
    struct list_head root;
};

//初始化
struct node *init_list(void)
{
    struct node *head = malloc(sizeof(struct node));   //头节点

    if(head != NULL)
    {
        INIT_LIST_HEAD(&head->root);         //引用"list.h" 让 list 指向自己
    }
    //这里就是头指针指向一个空链表
}

//创建新的节点
struct node *new_node(int data)
{
    struct node *new = calloc(1, sizeof(struct node));
    if(new != NULL)
    {
        new->data = data;   
    }
    return new;
}

//显示
void show(struct node *head)
{
    struct list_head *pos;                     //小结构体的指针 
    struct node *p;

    list_for_each(pos, &head->root)           //这个在内核链表宏定义是一个for循环
    {
        p = list_entry(pos, struct node, root);      //这个是从小结构体获得大结构体的指针
        printf("%d\t", p->data);
    }
    printf("\n");
}

//奇偶排列
void rearrange(struct node *head)
{
    struct list_head *pos, *tmp;
    struct node *p;

    int flag = 0;

    list_for_each_prev(pos, &head->root)             //不断的往前找
    {
        p = list_entry(pos, struct node, root);
        if(p->data % 2 == 0 && flag != 0)         // flag != 0 避免一开始就是偶数
        {
            list_move_tail(pos, &head->root);    //将 p 指向的节点移动到头节点之前
            pos = tmp;                           //如果是偶数  保存这个位置                  
        }
        else
        {
            tmp = pos;                       //如果是奇数 保存这个位置  防止出现死循环
        }
        flag = 1;   
    }

}

int main(int argc, char **argv)
{
    struct node *head;
    head = init_list();

    //获得用户输入的数据
    int n;
    scanf("%d", &n);

    int i;
    for(i = 0; i <= n; i++)
    {
        struct node *new = new_node(i);

        //注意:
        //1. new 和 head 是指向大结构体的
        //2.我们操作的是小结构体(也就是内核链表)
        //3.所以要加上取地址符号 & 
        list_add_tail(&new->root, &head->root);
    }

    show(head);

    rearrange(head);
    show(head);
    return 0;
}

运行结果:
LINUX内核链表的详细解析_第3张图片

你可能感兴趣的:(Linux基础笔记库)