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

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

一、环境准备

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

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

二、代码准备

将实验代码保存为 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.cMakefile所在目录执行:

复制代码
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
相关推荐
zzipeng几秒前
IMX6ULL CAN通讯应用学习
linux·运维·网络
代码中介商1 分钟前
Linux多线程编程完全指南(下):线程同步与互斥锁
linux·redis·线程·互斥锁
一个人旅程~3 分钟前
Win旧版或win10部分版本如何解除260字符长路径名限制?
linux·windows·经验分享·电脑
iEdHu7 分钟前
LinuxDO | L站 | Linux.do邀请码2026最新获取方式【邀请链接每日分享】
linux·经验分享·其他·社交电子
中国lanwp17 分钟前
CentOS 7 搭建 NFS Server 服务端 + 客户端 完整一键配置
linux·运维·centos
charlie11451419122 分钟前
嵌入式Linux驱动开发(8)——内存映射 I/O - 别拿物理地址当指针用
linux·开发语言·驱动开发·c·imx6ull
a2591748032-随心所记28 分钟前
android拆解super.img内容
android·linux·运维·服务器
实心儿儿38 分钟前
Linux —— 文件系统_路径解析_软硬连接
linux·运维·服务器
zzzb12345642 分钟前
CC-Switch 全平台下载、安装与使用全指南(Windows/macOS/Linux)
linux·windows·macos·ai编程·cc-switch
云达闲人44 分钟前
搭建DevOps企业级仿真实验环境:004Proxmox 内核调优与虚拟化优化
linux·服务器·devops·硬件加速·linux内核调优·虚拟化优化·内存气球