
在 Linux 系统中,所有硬件设备都会被当作文件来管理。这些设备对应的特殊文件叫做设备节点,都存放在 /dev 文件夹里。它们的作用就像桥梁:一边连接着系统内部识别的硬件设备,另一边让应用程序能像操作普通文件一样读写设备数据。
一、SYSFS 文件系统
从 Linux 2.6 版本开始,内核引入了一个叫 sysfs 的系统,它默认挂载在 /sys 目录下。这其实是一个存在内存里的文件系统,和 /proc 类似。它的主要作用是让内核和应用程序能方便地交换信息。
内核会通过 sysfs 把一些内部参数"暴露"出来。每个被暴露的参数在 /sys 目录里都会对应一个文件。
二、自动创建设备文件的工具
1. devtmpfs 这是 Linux 系统启动时自动创建设备文件的工具。开机时,系统会把 devtmpfs 挂载到 /dev 目录下,自动生成所有硬件设备对应的设备文件。
2. udev udev 是设备管理的"智能管家"。系统运行时,只要硬件插入或拔出,udev 就会收到通知,自动创建/删除对应的设备文件。
3. mdev mdev 是轻量级的设备管理工具(常用于嵌入式设备)。它在系统启动时创建设备文件,也能在硬件插拔时执行用户脚本。
总结区别
- devtmpfs:开机时快速生成基础设备文件,由内核直接管理。
- udev:功能强大,实时管理硬件变化,适合复杂系统。
- mdev:轻量简单,适合资源有限的设备(如路由器),但功能较少。
三、手动创建设备节点
使用 mknod 命令手动创建设备节点的语法如下:
mknod 设备名称 类型 主编号 次编号参数说明:
- 设备名称:你想创建的节点文件名(如
/dev/device_test) - 类型:用
b(块设备)、c(字符设备)或p(管道)表示设备类型 - 主编号:设备的主识别号
- 次编号:设备的次级识别号
例如:
mknod /dev/device_test c 511 0四、自动创建设备节点
设备文件的自动创建主要由 udev 或 mdev 机制完成,系统通常会自动处理。
具体过程分两步:
- 驱动程序先在系统中注册设备类别(这会在
/sys/class文件夹里生成一个类别标识) - 接着驱动程序创建具体设备信息。当驱动加载时,udev 会立刻感知到这个动作。
1、创建类
① class_create(...) 函数

该函数用于在 Linux 内核中动态创建设备类。参数说明:
- owner:指向模块的指针,通常填
THIS_MODULE - name:设备类的名称字符串(如
"my_device")
② class_destroy(...) 函数

2、创建设备
③ device_create(...) 函数
device_create(struct class *cls, struct device *parent, dev_t devt,
void *drvdata, const char *fmt, ...);2
用来在 class 类中下创建一个设备属性文件,udev 会自动识别从而进行设备节点的创建。
参数含义:
cls:指定所要创建的设备所从属的类parent:指定该设备的父设备,如果没有就指定为 NULLdevt:指定创建设备的设备号drvdata:被添加到该设备回调的数据,没有则指定为 NULLfmt:添加到系统的设备节点名称
④ device_destroy(...) 函数
void device_destroy(struct class *cls, dev_t devt);用来删除 class 类中的设备属性文件。
五、实验代码
源代码下载
git clone git@gitee.com:yangxuesong314/linux-driver.git- 代码位于:
linux-driver/05.字符设备驱动模型/03.创建设备节点实验
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/kdev_t.h>
#include <linux/cdev.h>
static dev_t dev_num;
static struct cdev cdev_test;
static struct file_operations cdev_fops_test = {
.owner = THIS_MODULE,
};
static struct class *class_test;
static int __init chrdev_fops_init(void)
{
int ret;
int major, minor;
ret = alloc_chrdev_region(&dev_num, 0, 1, "chrdev_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);
cdev_init(&cdev_test, &cdev_fops_test);
cdev_test.owner = THIS_MODULE;
ret = cdev_add(&cdev_test, dev_num, 1);
if (ret < 0) {
printk("cdev_add is error \n");
}
printk("cdev_add is ok \n");
class_test = class_create(THIS_MODULE, "class_test");
device_create(class_test, NULL, dev_num, NULL, "device_test");
return 0;
}
static void __exit chrdev_fops_exit(void)
{
cdev_del(&cdev_test);
unregister_chrdev_region(dev_num, 1);
device_destroy(class_test, dev_num);
class_destroy(class_test);
printk("module exit \n");
}
module_init(chrdev_fops_init);
module_exit(chrdev_fops_exit);
MODULE_LICENSE("GPL v2");2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
六、实验运行结果
