Heng30的博客
搜索 分类 关于 订阅

Linux驱动如何使用链表?

2025-03-18

链表是一种常用的数据结构,在开发中经常会使用到。在Linux内核中同样也是一个很常用的数据结构,而且内核对链表实现了很多相关函数,极大方便了链表的使用。内核中的链表和一般的链表使用方法是不一样的。内核中的链表成员是结构体中的一个成员,通过内存偏移的方法,可以通过链表成员的内存地址得到宿主结构体成员的内存地址。

结构体

struct list_head {
	struct list_head *next, *prev;
};

#define LIST_HEAD_INIT(name) { &(name), &(name) }

#define LIST_HEAD(name) struct list_head name = LIST_HEAD_INIT(name)

驱动代码

// simple.c

#include <linux/init.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/slab.h>

struct data {
    struct list_head list;
    int num;
};

LIST_HEAD(my_list);

static int __init simple_init(void) {
    pr_info("simple_init\n");

    for (int i = 0; i < 3; i++) {
        struct data *tmp = kmalloc(sizeof(struct data), GFP_KERNEL);
        tmp->num = i;
        list_add_tail(&tmp->list, &my_list);
    }

    struct list_head *ptr;
    list_for_each_prev(ptr, &my_list) {
        struct data *tmp = list_entry(ptr, struct data, list);
        pr_info("num = %d\n", tmp->num);
    }

    struct data *tmp, *next;
    list_for_each_entry_safe(tmp, next, &my_list, list) {
        list_del(&tmp->list);
        kfree(tmp);
    }

    return 0;
}

static void __exit simple_exit(void) { pr_info("simple_exit\n"); }

module_init(simple_init);
module_exit(simple_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("heng30");
MODULE_VERSION("v0.0.1");
MODULE_DESCRIPTION("A simple kernel module");

编译脚本

#!/bin/sh

top-dir = $(shell pwd)
kernel-version = $(shell uname -r)
kernel-dir ?= /lib/modules/$(kernel-version)/build

obj-m += simple.o

all:
        make -C $(kernel-dir) modules M=$(top-dir)

clean:
        rm -f *.o *.ko *.mod *.mod.c *.order *.symvers *.dtbo
        make -C $(kernel-dir) clean m=$(top-dir)

测试

  • 编译程序:make

  • 安装驱动:insmod simple.ko

  • 移除驱动:rmmod simple.ko

  • 查看输出:dmesg

[ 9806.982678] simple_init
[ 9806.982688] num = 2
[ 9806.982690] num = 1
[ 9806.982691] num = 0
[ 9815.804907] simple_exit

参考