这段代码是 Linux 内核模块中用于动态指定设备号的典型实现,适用于字符设备驱动开发。以下是逐行解释:
1. 定义静态设备号参数
static int major; // 静态主设备号(如 240)
static int minor; // 静态次设备号(如 0)
- 作用:声明两个静态整型变量,用于存储从模块参数传递的主/次设备号。
- 静态关键字:
static表示变量仅在当前文件内可见,避免命名冲突。
2. 暴露模块参数
module_param(major, int, S_IRUGO); // 允许通过 insmod/modprobe 传递 major 参数
module_param(minor, int, S_IRUGO); // 允许通过 insmod/modprobe 传递 minor 参数
module_param宏:将变量声明为模块参数,支持在加载模块时动态赋值。- 参数1:变量名(
major,minor)。 - 参数2:参数类型(
int)。 - 参数3:权限掩码(
S_IRUGO表示只读,对应/sys/module/<模块名>/parameters/下的文件权限)。
- 参数1:变量名(
- 使用示例:
sudo insmod my_driver.ko major=240 minor=0
3. 定义设备号变量
static dev_t dev_num; // 用于存储组合后的设备号(32/64 位,取决于内核配置)
dev_t类型:内核中表示设备号的类型,包含主设备号(Major)和次设备号(Minor)。- 主设备号:标识设备类型(如字符设备、块设备),由内核分配。
- 次设备号:由驱动自行管理,用于区分同类型的不同设备实例。
4. 典型使用场景
在模块初始化函数(如 module_init)中,通常会执行以下操作:
static int __init my_driver_init(void) {
// 组合主次设备号为 dev_t
dev_num = MKDEV(major, minor);
// 注册字符设备(静态分配方式)
register_chrdev_region(dev_num, 1, "my_device");
// 或者动态分配设备号(更推荐)
// alloc_chrdev_region(&dev_num, minor, 1, "my_device");
// 创建类、设备节点等操作...
return 0;
}
5. 关键注意事项
- 静态分配 vs 动态分配:
- 静态分配:需手动指定
major,需确保未被其他驱动占用(通过cat /proc/devices查询)。 - 动态分配:使用
alloc_chrdev_region让内核自动分配空闲设备号,更安全。
- 静态分配:需手动指定
- 设备号范围:
- 传统 8+24 位:主设备号 8 位(0-255),次设备号 24 位。
- 64 位 dev_t:现代内核支持更大范围,但主设备号仍建议使用
<256。
- 权限管理:
S_IRUGO确保参数文件在/sys/module/下为只读,防止运行时误修改。
6. 完整流程示例
// 1. 定义参数和设备号变量
static int major;
static int minor;
module_param(major, int, S_IRUGO);
module_param(minor, int, S_IRUGO);
static dev_t dev_num;
// 2. 模块初始化函数
static int __init my_driver_init(void) {
int ret;
// 组合设备号
dev_num = MKDEV(major, minor);
// 静态注册设备号(需确保 major 未被占用)
ret = register_chrdev_region(dev_num, 1, "my_device");
if (ret < 0) {
printk(KERN_ERR "Failed to register device number\n");
return ret;
}
// 动态分配示例(更推荐)
// ret = alloc_chrdev_region(&dev_num, minor, 1, "my_device");
// major = MAJOR(dev_num); // 从分配结果中提取主设备号
// 创建类、设备节点等...
return 0;
}
// 3. 模块退出函数
static void __exit my_driver_exit(void) {
unregister_chrdev_region(dev_num, 1);
// 清理资源...
}
module_init(my_driver_init);
module_exit(my_driver_exit);
7. 用户空间交互
加载模块后,用户可通过 mknod 手动创建设备节点:
sudo mknod /dev/my_device c $(major) $(minor)
c表示字符设备。- 动态分配时,需从
/proc/devices或dmesg输出中获取分配的major。
通过这种方式,驱动代码实现了设备号的灵活配置,兼顾了静态指定的直接性和动态分配的安全性。

被折叠的 条评论
为什么被折叠?



