Linux 字符设备驱动中 “主次设备号的静态 / 动态分配” 实验

这个实验是Linux 字符设备驱动中 "主次设备号的静态 / 动态分配" 实验,核心是验证 "手动指定设备号" 和 "内核自动分配设备号" 两种方式,步骤如下:

一、环境准备

和之前的信号量实验一致:

  1. 安装对应架构的交叉编译工具链(如aarch64-linux-gnu-)。
  2. 准备开发板对应的Linux 内核源码 ,记录其路径(后续Makefile中KDIR需填写)。

二、代码准备

将实验代码保存为 dev_t_test.c(代码与实验一致,仅补全注释):

cpp 复制代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/kdev_t.h>

static int major; //静态分配时的主设备号
static int minor; //静态分配时的次设备号

//向模块传递参数(主/次设备号 权限为用户可读)
module_param(major,int,S_IRUGO);
module_param(minor,int,S_IRUGO);

static dev_t dev_num; //存储设备号,主设备和从设备的组合

//驱动入口函数,分配设备号、
static int __init dev_t_init(void)
{
    int ret;
    //情况1:手动传入了major参数 静态分配设备号
    if(major){
        dev_num = MKDEV(major,minor);
        printk("major is %d\n",major);
        printk("minor is %d\n",minor);

        //静态分配设备号
        /*int register_chrdev_region(dev_t first, unsigned int count,  \ 
                          char *name);
        参数:
            1. dev_t first: 起始的设备号,包括主设备号 (MAJOR,默认最大511) 和次设备号 (MINOR)。
            2. unsigned count: 需要分配的连续设备号的数量。
            3. const char *name: 注册的设备名称,用于用户态与内核交互时的设备识别。
        返回值:
            - 返回 0 表示成功。
            - 如果失败,返回一个负的错误码。
        */
        ret = register_chrdev_region(dev_num,1,"major_minor_name");
        if(ret < 0){
            printk("register_chrdev_region is error\n");
        }
            printk("register_chrdev_region is ok\n");
    }
    //情况2:未传入major参数 动态分配设备号
    else{
        //动态分配设备号,起始设备号为0
        /*
        int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,
                        const char *name)
        参数解析:
        1. dev_t *dev:
        - 用于返回分配的设备号(包含主设备号和次设备号)。
        - 主设备号通过自动分配的方式确定。
        - 次设备号起始值通过 baseminor 指定。
        2. unsigned baseminor:
        - 指定起始的次设备号。
        3. unsigned count:
        - 要分配的连续设备号的数量。
        4. const char *name:
        - 设备的名称,用于用户态和内核的设备识别。
        返回值:
        - 返回 0 表示成功。
        - 返回负数表示失败,失败值是一个错误码(通过 PTR_ERR 提供)。
        */
        ret = alloc_chrdev_region(&dev_num,0,1,"major_minor_name");
        if(ret < 0){
            printk("alloc_chrdev_region is error\n");
        }
            printk("alloc_chrdev_region is ok\n");
            major = MAJOR(dev_num);
            minor = MINOR(dev_num);
            printk("major is %d\n",major);
            printk("minor is %d\n",minor);
    }
        return 0;

}

//驱动出口函数,释放设备号
static void __exit dev_t_exit(void)
{
    unregister_chrdev_region(dev_num,1);
    printk("guo module exit\n");

}

module_init(dev_t_init);//注册入口函数
module_exit(dev_t_exit);//注册出口函数
MODULE_LICENSE("GPL v2");//遵循GPL协议

三、编写 Makefile(编译驱动模块)

创建Makefile文件(注意替换KDIR为你的内核源码路径):

makefile

cpp 复制代码
# 1. 补充:导出ARM64架构(必须)
export ARCH=arm64
# 2. 补充:用export导出交叉编译器(必须,子进程才能继承)
export CROSS_COMPILE=/home/alientek/rk3568_linux5.10_sdk/buildroot/output/rockchip_atk_dlrk3568/host/bin/aarch64-buildroot-linux-gnu-
# 内核源码目录(你的路径保持不变)
KERNELDIR := /home/alientek/rk3568_linux5.10_sdk/kernel
# 当前目录(你的定义保持不变)
CURRENT_PATH := $(shell pwd)
# 要编译的模块(你的定义保持不变)
obj-m := dev_t_test.o

# 目标定义(你的形式保持不变)
build: kernel_modules

kernel_modules:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

四、编译驱动模块

在dev_t_test.c和Makefile所在目录执行:

复制代码
make

编译完成后,会生成 dev_t_test.ko(驱动模块文件)。

五、部署与测试(开发板上操作)

将dev_t_test.ko传到开发板,然后分两种情况测试:

情况 1:静态分配设备号(手动指定 major/minor)

加载模块时直接传入主 / 次设备号(比如major=200,minor=0):

复制代码
insmod dev_t_test.ko major=200 minor=0

执行dmesg查看输出,会显示:

plaintext

复制代码
major is 200
minor is 0
register_chrdev_region is ok
情况 2:动态分配设备号(不指定参数)

直接加载模块,内核自动分配设备号: 如果手动分配主设备号20000失败

动态分配了3616

复制代码
rmmod dev_t_test  # 先卸载之前的模块
insmod dev_t_test.ko

执行dmesg查看输出,会显示内核分配的主 / 次设备号(比如):

plaintext

复制代码
alloc_chrdev_region is ok
major is 240
minor is 0

六、收尾

测试完成后,卸载驱动模块:

复制代码
rmmod dev_t_test
相关推荐
我科绝伦(Huanhuan Zhou)5 小时前
Linux 通用压力测试脚本合集:磁盘 + 网络
linux·网络·压力测试
Soneswy7 小时前
Linux系统篇--重定向与文件操作命令
linux
有毒的教程7 小时前
Linux底层存储路由配
linux·运维·网络
Linux系统运维工程师_郭主任8 小时前
运维人还在被拒offer?现在能考的,只有 RHCE 10.0。
linux
自养Agent8 小时前
MCP Server Boundary Audit Report
linux·服务器·人工智能·windows·大模型·llm
Coisinier9 小时前
docker命令管理镜像及容器
linux·运维·nginx·docker·容器·操作系统
A心有千千结10 小时前
通过 SSH 反向代理让服务器借用本机网络访问 GitHub 的最佳实践
linux·服务器·网络·git·ssh·github
书生执笔画浮沉10 小时前
Linux 内核模块为什么不能自己解决兼容性问题?
linux·c·kernel·update·灵感
15Moonlight11 小时前
Linux网络(03):Socket 编程 TCP
linux·网络·tcp/ip
广州灵眸科技有限公司11 小时前
单芯片同时跑 Linux 与 RT-Thread:RV1126B的AMP异构方案完整拆解
linux·运维·服务器