当前位置:首页>Linux>Linux内核链表 双向循环链表 0x00

Linux内核链表 双向循环链表 0x00

  • 2026-09-02 19:45:46
Linux内核链表 双向循环链表 0x00

一、一些概念

1.Linux内核链表是内嵌式双向循环链表,核心是struct list_head,只存指针、不存数据,通过嵌入宿主结构体实现类型无关、高效统一的链表操作;

2.核心结构:

//在include/linux/types.h中定义struct list_head{    struct list_head *next, *prev;};1)只有两个指针:next(下一个)、prev(上一个);2)无数据域,嵌入到业务结构体中使用;3)双向循环:空链表时 next/prev 都指向自己。

二、常用操作(在include/linux/list.h中定义)

1.链表初始化

1)静态初始化 //定义并初始化一个链表头  LIST_HEAD(my_list_head);2)动态初始化  struct list_head my_list_head;  INIT_LIST_HEAD(&my_list_head);

2.插入节点

1)头插(栈):list_add(new, head);2)尾插(队列):list_add_tail(new, head);

3.删除节点

  //删除指定节点  list_del(&node->list);  //删除并初始化节点(安全)  list_del_init(&node->list);

4.链表判断

//判断是否为空list_empty(&my_list_head);//判断是否只有一个节点list_is_singular(&my_list_head);

5.链表合并/切割

//合并链表(头接)list_splice(&list1, &my_list_head);//合并链表(尾接)list_splice_tail(&list1, &my_list_head);

6.遍历链表

  1)基础遍历(指针级)struct list_head *pos;list_for_each(pos, &my_list_head) {    // pos 是 struct list_head*    struct my_data *data = list_entry(pos, struct my_data, list);    // 访问 data->id, data->name}  2)直接遍历宿主(推荐)struct my_data *pos;list_for_each_entry(pos, &my_list_head, list) {    // pos 直接是 struct my_data*    printk("id: %d, name: %s\n", pos->id, pos->name);}  3)安全遍历(遍历中可删除节点)struct my_data *pos, *n;list_for_each_entry_safe(pos, n, &my_list_head, list) {    // 可安全删除 pos    list_del(&pos->list);    kfree(pos);}  4)反向遍历list_for_each_entry_reverse(pos, &my_list_head, list);list_for_each_entry_safe_reverse(pos, n, &my_list_head, list);

三、测试

1.源码

listTestDrv.c

#include<linux/init.h>#include<linux/module.h>#include<linux/list.h>#include<linux/slab.h>// 1.定义宿主结构体struct my_data {    int id;    char name[32];    struct list_head list;};// 2.定义链表头staticLIST_HEAD(my_data_list);staticint __init list_demo_init(void){    int i;    struct my_data *data;struct my_data *n;    //3.创建并添加节点    for (i = 0; i < 3; i++) 	{        data = kmalloc(sizeof(*data), GFP_KERNEL);        data->id = i;        snprintf(data->name, sizeof(data->name), "node-%d", i);        list_add_tail(&data->list, &my_data_list);  // 尾插    }    //4.遍历并打印    printk("=== Traverse list ===\n");    list_for_each_entry(data, &my_data_list, list)	{        printk("id: %d, name: %s\n", data->id, data->name);    }    //5.安全删除所有节点    printk("=== Delete all nodes ===\n");    list_for_each_entry_safe(data, n, &my_data_list, list)	{        list_del(&data->list);        kfree(data);    }    return 0;}staticvoid __exit list_demo_exit(void){    printk("Module exit\n");}module_init(list_demo_init);module_exit(list_demo_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("AXUN");MODULE_DESCRIPTION("list test");

2.Makefile文件

CC = gcc-12  #指定编译器为gcc-12obj-m += listTestDrv.o  # 要编译的模块名(对应listTestDrv.c)KERNELDIR ?= /lib/modules/$(shell uname -r)/build  #内核源码路径PWD := $(shell pwd)  #当前目录all:$(MAKE) -C $(KERNELDIR) M=$(PWD) modules  #编译模块clean:$(MAKE) -C $(KERNELDIR) M=$(PWD) clean  #清理编译产物 

3.编译模块

执行编译命令make,编译成功后会生成listTestDrv.ko。

4.加载/卸载模块

#加载模块(需要root权限)

sudo insmod listTestDrv.ko

#查看已加载的模块(.ko文件)

sudo lsmod

#卸载模块

sudo rmmod listTestDrv

最新文章

随机文章