B站相应的视屏教程:
内核:博文+视频 - 备树深度解析:理论 + 实践全指南(含 of 函数与 i.MX8MP 实例)
敬请关注,记得标为原始粉丝。
本文目标:深入理解
device_create()
函数的核心机制,搞清楚字符设备驱动中/dev/xxx
是如何自动生成的,掌握class
、device
、sysfs
、udev
之间的协作原理。本文面向 Linux 内核驱动开发初学者,全流程分析字符设备注册背后的关键路径,夯实设备模型的理解。
在我们编写 Linux 字符设备驱动时,经常会使用如下流程注册设备:
alloc_chrdev_region();
cdev_init();
cdev_add();
class_create();
device_create(); // 核心
完成这些步骤后,系统会自动在 /dev/
下生成对应设备节点,例如 /dev/my_led
。
但很多初学者会疑惑:
device_create()
就能生成 /dev/xxx
?struct device
?udev
又是什么关系?本篇将一口气讲清楚这些问题。
Linux 的设备模型中,class
是连接用户空间 /sys/class/
与 /dev/
的关键中介,它代表某一类设备。
常见 class:
类别 | 描述 | 示例路径 |
---|---|---|
tty |
终端设备 | /sys/class/tty/ttyS0 |
input |
输入设备 | /sys/class/input/event0 |
leds |
LED 灯控制 | /sys/class/leds/user_led |
你在驱动中调用:
struct class *led_class = class_create(THIS_MODULE, "my_led_class");
内核会在 /sys/class/my_led_class/
下创建一个新目录,为后续设备挂载做准备。
接下来进入主角——device_create()
。
struct device *device_create(struct class *class, struct device *parent,
dev_t devt, void *drvdata, const char *fmt, ...);
参数说明:
参数 | 含义 |
---|---|
class | 所属的 class(如 my_led_class) |
parent | 父设备,可为 NULL |
devt | 设备号,MKDEV(major, minor) |
drvdata | 设备私有数据,可用 device_get_drvdata 获取 |
fmt | 设备名称格式,如 “my_led%d” |
执行 device_create()
会做三件事:
struct device
class
,即:/sys/class/xxx/
下出现新设备目录udev
创建 /dev/xxx
节点这正是用户空间中能自动看到 /dev/my_led0
的原因。
我们通过一张图总结:
驱动代码
└── device_create()
├── 创建设备结构体 struct device
├── 添加到 class 的设备列表
│ └── 映射到 /sys/class/my_class/my_led0
├── 发出 uevent 通知
│ └── 用户空间 udev 监听到设备加入事件
│ └── 根据规则创建 /dev/my_led0
举个实际路径:
/sys/class/my_led_class/my_led0 # class 目录
/dev/my_led0 # 实际设备节点(由 udev 创造)
dev_t devno;
alloc_chrdev_region(&devno, 0, 1, "my_led");
major = MAJOR(devno);
cdev_init(&my_cdev, &my_fops);
cdev_add(&my_cdev, devno, 1);
my_class = class_create(THIS_MODULE, "my_led_class");
device_create(my_class, NULL, devno, NULL, "my_led0");
此时:
/sys/class/my_led_class/my_led0
被创建/dev/my_led0
自动生成(由 udev
监听创建设备事件)udev
是 Linux 中用户空间的设备管理守护进程。
uevent
)/etc/udev/rules.d/
的规则,执行创建设备节点、添加权限等操作若系统中没有运行 udev
(如部分嵌入式系统),需要用 mdev
或手动创建设备文件:
mknod /dev/my_led0 c 240 0
device_create()
,是否还能访问设备?可以,但必须自己创建设备节点:
mknod /dev/my_led0 c 240 0
并确保 major/minor 正确。但 /sys/class/
等路径就不会自动生成。
你可以多次调用 device_create()
:
device_create(my_class, NULL, MKDEV(240, 0), NULL, "my_led0");
device_create(my_class, NULL, MKDEV(240, 1), NULL, "my_led1");
这样就会生成 /dev/my_led0
和 /dev/my_led1
。
在模块卸载时:
device_destroy(my_class, MKDEV(240, 0));
class_destroy(my_class);
unregister_chrdev_region(MKDEV(240, 0), 1);
核心步骤 | 功能 |
---|---|
class_create | 创建设备类别,挂载到 /sys/class |
device_create | 注册 struct device,生成 /sys 和 /dev 节点 |
udev | 监听内核事件,自动创建设备文件 |
dev_set_drvdata | 设置私有数据,可用 device_get_drvdata 获取 |
cdev 操作 | 管理字符设备的核心操作结构 |
/dev/led0
和 /dev/led1
write()
控制两个设备状态,打印开关日志udevadm monitor
查看 device_create()
时产生的事件下一篇预告:
驱动开发硬核特训 · Day 14:深入理解 class、device 与 /dev 的完整链路
将进一步拆解 class.c
源码,讲清 device_add()
、kobject
关系,帮助你彻底掌握设备文件生成机制。
如果这篇内容帮助到你,欢迎点赞、收藏、转发!
B站:嵌入式Jerry
CSDN 博客:嵌入式Jerry
下一篇,见!