暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

一起撸linux内核08-链表和变态container_of02

囧囧妹 2022-02-19
120



一、开篇

上一节我们说了链表的初始化、增删查,那么怎么用呢?这里刨个坑,我们先给出示例之后再来说变态的container_of,因为我们要获取链表节点对应的数据域就是通过它获取的。

示例代码,我们写一个内核模块,该模块用来测试链表的增删查,同时我们再定义一个结构体,该结构体包含数据域和链表节点。

二、示例

内核模块c,test_list.c


    /**
    * @file test_list.c
    * @brief 内核链表测试
    * @author zshare
    * @version v1.0
    * @date 2022-02-16
    * @history
    */


    #include <linux/module.h>//对应内核源码中include/linux/module.h
    #include <linux/moduleparam.h>
    #include <linux/kernel.h>
    #include <linux/init.h> 包含了module_init和module_exit的声明
    #include <linux/cdev.h> 包含字符设备操作函数
    #include <linux/fs.h> 包含文件接口操作函数
    #include <linux/uaccess.h>
    #include <asm/atomic.h>
    #include <linux/slab.h>


    #define _log_
    #ifdef _log_
    #define kernel_print(format, arg...) printk(format,## arg)
    #else
    #define kernel_print(format, arg...) do{}while(0)
    #endif
    #define VERSION "V1.0"
    typedef unsigned char u8;
    typedef unsigned short u16;
    typedef unsigned int u32;
    #define nil NULL
    #define FALSE 1
    #define TRUE 0
    #define pcalloc(x,size,type) do{ if((x = (type *)kcalloc(1,size,GFP_KERNEL)) == nil){ return FALSE; } }while(0)
    #define pfree(ptr) do{ if(ptr != nil) { kfree(ptr); } ptr = nil; }while(0)


    //一个身份节点
    typedef struct {
    u32 id;//成员id
    u8 name[16];//成员名字
    u8 text[32];//成员hash标识
    struct list_head node;//定义一个链表节点
    }id_st;


    u32 init_id_node(u8 *name, u8 *text, id_st **pid){
    static u32 id = 0;
    id_st *ptmp = nil;
    申请一个身份节点
    pcalloc(ptmp, sizeof(id_st), id_st);
    给节点赋值
    ptmp->id = ++id;
    memcpy(ptmp->name, name, 16);
    memcpy(ptmp->text, text, 31);
    初始化链表节点
    INIT_LIST_HEAD(&ptmp->node);
    (*pid) = ptmp;
    return 0;
    }


    //打印链表
    void print_list(const struct list_head *plist){
    id_st *pos = nil;
    do{
    if(list_empty(plist)){ kernel_print("\t链表为空!\n"); break; }
    //对链表进行遍历操作并打印数据域
    list_for_each_entry(pos, plist, node){
    kernel_print("\t[%d]:%s,%s.\n", pos->id, pos->name, pos->text);
    }
    }while(0);
    }


    static int __init tlist_init(void){
    int ret = -1;
    id_st *a = nil;
    id_st *b = nil;
    id_st *c = nil;


    //定义一个身份链表
    struct list_head id_list;
    //初始化3个身份节点,分别是A,B,C
    ret = init_id_node("AAAAAAAAAAAAAAA", "a hash value", &a);
    ret = init_id_node("BBBBBBBBBBBBBBB", "b hash value", &b);
    ret = init_id_node("CCCCCCCCCCCCCCC", "c hash value", &c);


    /*==========链表测试开始==========*/
    //初始化链表
    INIT_LIST_HEAD(&id_list);
    //将三个节点采用头插插入链表
    list_add(&a->node, &id_list);
    list_add(&b->node, &id_list);
    list_add(&c->node, &id_list);
    //打印
    kernel_print("插入三个成员后:\n");
    print_list(&id_list);


    //删除b节点再打印
    list_del(&b->node);
    //再打印
    kernel_print("删除b成员后:\n");
    print_list(&id_list);
    //注意需要释放掉删除的节点
    pfree(b);


    //删除a节点再打印
    list_del(&a->node);
    //再打印
    kernel_print("删除a成员后:\n");
    print_list(&id_list);
    //注意需要释放掉删除的节点
    pfree(a);


    //删除c节点再打印
    list_del(&c->node);
    //再打印
    kernel_print("删除c成员后:\n");
    print_list(&id_list);
    //注意需要释放掉删除的节点
    pfree(c);


    return ret;
    }


    static void __exit tlist_exit(void){


    }


    module_init(tlist_init);
    module_exit(tlist_exit);


    MODULE_LICENSE("GPL");//软件代码接受的许可协议General Public License
    MODULE_AUTHOR("Share");//声明作者
    MODULE_DESCRIPTION("A test list module");//模块简单的描述
    MODULE_VERSION(VERSION);//模块版本
    MODULE_ALIAS("tlist");//模块在用户空间的别名


    编译完成先将dmesg c来清除打印缓存,通过insmod插入

    我们来通过dmesg来查看结果是否和我们程序设计的结果一样。

    通过dmesg我们发现和预期是一样的,我们初始化了一个head,然后插入三个成员,遍历打印链表,然后先删除中间节点然后是a、c节点,删除后分别遍历打印发现结果符合预期。
    好了,我们通过一个简单的示例来展示了一下内核链表的简单使用。那么我们翻回去填之前的坑,如何通过链表节点来获取数据域?先到这里,只能明天继续了,码农得继续加班写文件系统了。

    文章转载自囧囧妹,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

    评论