🔍
B站相应的视屏教程 :
📌 内核:博文+视频 - 备树深度解析:理论 + 实践全指南(含 of 函数与 i.MX8MP 实例)
敬请关注,记得标为原始粉丝。
🔧
📌 本文目标:深入理解
device_create()
函数的核心机制,搞清楚字符设备驱动中/dev/xxx
是如何自动生成的,掌握class
、device
、sysfs
、udev
之间的协作原理。本文面向 Linux 内核驱动开发初学者,全流程分析字符设备注册背后的关键路径,夯实设备模型的理解。
一、背景导入:为何要讲 device_create?

在我们编写 Linux 字符设备驱动时,经常会使用如下流程注册设备:
c
alloc_chrdev_region();
cdev_init();
cdev_add();
class_create();
device_create(); // 核心
完成这些步骤后,系统会自动在 /dev/
下生成对应设备节点,例如 /dev/my_led
。
但很多初学者会疑惑:
- 为什么调用
device_create()
就能生成/dev/xxx
? - 它是怎么和内核设备模型联系上的?
- 它是否会注册
struct device
? - 跟
udev
又是什么关系?
本篇将一口气讲清楚这些问题。
二、设备模型再复习:class 是什么?
Linux 的设备模型中,class
是连接用户空间 /sys/class/
与 /dev/
的关键中介,它代表某一类设备。
常见 class:
类别 | 描述 | 示例路径 |
---|---|---|
tty |
终端设备 | /sys/class/tty/ttyS0 |
input |
输入设备 | /sys/class/input/event0 |
leds |
LED 灯控制 | /sys/class/leds/user_led |
你在驱动中调用:
c
struct class *led_class = class_create(THIS_MODULE, "my_led_class");
内核会在 /sys/class/my_led_class/
下创建一个新目录,为后续设备挂载做准备。
接下来进入主角------device_create()
。
三、device_create() 的功能与原型
3.1 函数原型
c
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" |
3.2 作用
执行 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
举个实际路径:
bash
/sys/class/my_led_class/my_led0 # class 目录
/dev/my_led0 # 实际设备节点(由 udev 创造)
五、实战演练:创建 LED 字符设备
5.1 初始化设备号
c
dev_t devno;
alloc_chrdev_region(&devno, 0, 1, "my_led");
major = MAJOR(devno);
5.2 初始化 cdev
c
cdev_init(&my_cdev, &my_fops);
cdev_add(&my_cdev, devno, 1);
5.3 创建设备 class 与 device
c
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 的作用与补充说明
udev
是 Linux 中用户空间的设备管理守护进程。
- 它监听内核发出的设备事件(
uevent
) - 根据
/etc/udev/rules.d/
的规则,执行创建设备节点、添加权限等操作
若系统中没有运行 udev
(如部分嵌入式系统),需要用 mdev
或手动创建设备文件:
bash
mknod /dev/my_led0 c 240 0
七、常见问题解答 Q&A
Q1:如果不调用 device_create()
,是否还能访问设备?
可以,但必须自己创建设备节点:
bash
mknod /dev/my_led0 c 240 0
并确保 major/minor 正确。但 /sys/class/
等路径就不会自动生成。
Q2:多个设备如何创建多个节点?
你可以多次调用 device_create()
:
c
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
。
Q3:如何自动清理?
在模块卸载时:
c
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
👉 下一篇,见!