Linux驱动——MISC驱动

一、MISC驱动简介

misc——miscellaneous,译为杂项。众所周知,Linux 中的驱动是分层设计的,每个设备都有自己的归属类型,比如键盘为输入设备,属于 input 子系统。但是会存在一些设备无法简单被划分到某一类中,此时可以基于 misc 驱动框架实现该设备的驱动。misc 驱动为不知类型的设备提供一个框架,方便对应设备驱动的开发。

MISC 驱动框架特点:

  1. misc 驱动为字符设备驱动;
  2. 主设备号为 10;
  3. 简单易用。无需主动调用 register_chrdev_region()、cdev_add() 等函数,只需调用 misc_register() 即可完成次设备号的分配和设备文件的创建工作;
  4. 使用 platform 总线实现 misc 驱动和设备的绑定。

上一篇讲了字符设备驱动,MISC驱动可以看成字符设备驱动的一个“轻量封装”:

1
2
3
4
5
6
7
8
9
10
11
普通字符设备:
分配设备号
注册 cdev / register_chrdev
创建 class
创建 device
创建 /dev 节点

MISC 设备:
填一个 struct miscdevice
调用 misc_register()
内核帮你创建 /dev 节点

MISC 不是要和字符设备并列的新机制,它本质上仍然是字符设备。只是很多简单字符设备共享同一个主设备号 10,内核帮你管理次设备号和 /dev 节点。

用户态不关心驱动内部用的是 miscdevice 还是 register_chrdev。它只看到设备文件:

1
ls -l /dev/misc_lab

正常情况下会看到:

1
crw-rw-rw- 1 root root 10, 119 ... /dev/misc_lab

含义:

部分 含义
c 字符设备
10 misc 设备常用主设备号
119 动态分配的 misc 次设备号
/dev/misc_lab 用户态访问入口

用户态程序只需要:

1
2
3
int fd = open("/dev/misc_lab", O_RDWR);
write(fd, "1", 1);
close(fd);

驱动内部再把这几个系统调用映射到:

1
2
3
4
open()  -> misc_lab_open()
read() -> misc_lab_read()
write() -> misc_lab_write()
close() -> misc_lab_release()

查看系统里的 misc 设备:

1
cat /proc/misc

会看到类似:

1
2
3
4
 56 fuse
57 loop-control
60 cpu_dma_latency
119 misc_lab

这里:

1
2
119 是次设备号
misc_lab 是设备名

对应的设备节点就是:

1
/dev/misc_lab

二、struct miscdevice

MISC 驱动的入口是一个结构体:

1
2
3
4
5
6
static struct miscdevice my_misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = "misc_lab",
.fops = &misc_lab_fops,
.mode = 0666,
};

字段含义:

字段 含义
.minor 次设备号。MISC_DYNAMIC_MINOR 表示让内核动态分配
.name 设备名,决定 /dev/<name>
.fops file_operations,用户态系统调用的函数表
.mode 设备节点权限,例如 0666

注册:

1
2
3
4
5
ret = misc_register(&my_misc);
if (ret) {
pr_err("failed to register misc device: %d\n", ret);
return ret;
}

注销:

1
misc_deregister(&my_misc);

misc_register() 成功后,内核会帮你:

  • 分配一个 misc minor。
  • 把设备注册到字符设备层。
  • 使用主设备号 10。
  • 让 udev/devtmpfs 创建 /dev/misc_lab。

三、MISC 和 register_chrdev 的关系

上一篇 char_lab 使用传统字符设备写法:

1
2
3
4
5
6
ret = register_chrdev(0, "char_lab", &char_lab_fops);
char_lab_major = ret;
char_lab_devno = MKDEV(char_lab_major, 0);

char_lab_class = class_create(THIS_MODULE, "char_lab");
device_create(char_lab_class, NULL, char_lab_devno, NULL, "char_lab");

这一篇 misc_lab 使用 MISC 写法:

1
2
3
4
5
6
misc_lab_device.misc.minor = MISC_DYNAMIC_MINOR;
misc_lab_device.misc.name = "misc_lab";
misc_lab_device.misc.fops = &misc_lab_fops;
misc_lab_device.misc.mode = 0666;

misc_register(&misc_lab_device.misc);

对比:

写法 主设备号 /dev 节点 代码量 适合
register_chrdev 动态分配 需要 class/device 或手动 mknod 中 学习字符设备传统流程
miscdevice 固定主设备号 10 内核自动创建 少 简单单节点字符设备
手写 cdev 灵活 需要 class/device 多 正式复杂驱动

可以把 MISC 理解为:

1
2
3
4
5
miscdevice
= 字符设备注册
+ 固定 major 10
+ 动态 minor
+ 自动 /dev 节点

它不是全新的设备模型,而是对常见小字符设备场景的封装。

四、MISC 驱动 API

当设置好miscdevice以后就需要使用misc_register函数向系统中注册一个MISC设备,此函数原型如下:

1
int misc_register(struct miscdevice * misc)

函数参数和返回值含义如下: - misc:要注册的MISC设备。 - 返回值:负数,失败;0,成功。

以前需要自己调用一堆的函数去创建设备,比如在以前的字符设备驱动中会使用如下几个函数完成设备创建过程:

1
2
3
4
5
alloc_chrdev_region(); /* 申请设备号 */ 
cdev_init(); /* 初始化cdev */
cdev_add(); /* 添加cdev */
class_create(); /* 创建类 */
device_create(); /* 创建设备 */

现在可以直接使用 misc_register 一个函数来完成以上代码中的这些步骤。当卸载设备驱动模块的时候需要调用misc_deregister 函数来注销掉 MISC设备,函数原型如下:

1
int misc_deregister(struct miscdevice *misc)

函数参数和返回值含义如下:

  • misc:要注销的MISC设备。
  • 返回值:负数,失败;0,成功。

以前注销设备驱动的时候,需要调用一堆的函数去删除此前创建的cdev、设备等等内容,如下所示:

1
2
3
4
cdev_del(); /* 删除cdev */ 
unregister_chrdev_region(); /* 注销设备号 */
device_destroy(); /* 删除设备 */
class_destroy(); /* 删除类 */

现在只需要一个 misc_deregister 函数即可完成以上代码中的这些工作。

五、实例

功能:

1
2
3
4
5
write "1" -> 状态置 1
write "0" -> 状态置 0
read -> 返回当前状态
open -> dmesg 打印打开次数
release -> dmesg 打印 closed

misc_lab.c:

1
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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
// SPDX-License-Identifier: GPL-2.0

#include <linux/atomic.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/uaccess.h>

#define MISC_LAB_NAME "misc_lab"

struct misc_lab {
struct miscdevice misc;
int value;
atomic_t open_count;
};

static struct misc_lab misc_lab_device;

static int misc_lab_open(struct inode *inode, struct file *file)
{
int count = atomic_inc_return(&misc_lab_device.open_count);

pr_info(MISC_LAB_NAME ": opened %d times\n", count);
return 0;
}

static int misc_lab_release(struct inode *inode, struct file *file)
{
pr_info(MISC_LAB_NAME ": closed\n");
return 0;
}

static ssize_t misc_lab_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
char kbuf[8] = {0,};

if (count == 0 || count >= sizeof(kbuf)) {
return -EINVAL;
}

if (copy_from_user(kbuf, buf, count)) {
return -EFAULT;
}

if (kbuf[0] == '1') {
misc_lab_device.value = 1;
} else if (kbuf[0] == '0') {
misc_lab_device.value = 0;
} else {
return -EINVAL;
}

return count;
}

static ssize_t misc_lab_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
char kbuf[4];
int len;

if (*ppos > 0) {
return 0;
}

len = scnprintf(kbuf, sizeof(kbuf), "%d\n", misc_lab_device.value);
if (count < len) {
return -EINVAL;
}

if (copy_to_user(buf, kbuf, len)) {
return -EFAULT;
}

*ppos += len;
return len;
}

static const struct file_operations misc_lab_fops = {
.owner = THIS_MODULE,
.open = misc_lab_open,
.release = misc_lab_release,
.read = misc_lab_read,
.write = misc_lab_write,
.llseek = no_llseek,
};

static int __init misc_lab_init(void)
{
int ret;

misc_lab_device.value = 0;
atomic_set(&misc_lab_device.open_count, 0);

misc_lab_device.misc.minor = MISC_DYNAMIC_MINOR; // 让内核自动分配一个未使用的 misc minor
misc_lab_device.misc.name = MISC_LAB_NAME; // /dev节点名称
misc_lab_device.misc.fops = &misc_lab_fops;
misc_lab_device.misc.mode = 0666;

ret = misc_register(&misc_lab_device.misc);
if (ret) {
pr_err(MISC_LAB_NAME ": failed to register misc device: %d\n", ret);
return ret;
}

pr_info(MISC_LAB_NAME ": loaded, /dev/%s ready\n", MISC_LAB_NAME);
return 0;
}

static void __exit misc_lab_exit(void)
{
misc_deregister(&misc_lab_device.misc);
pr_info(MISC_LAB_NAME ": unloaded\n");
}

module_init(misc_lab_init);
module_exit(misc_lab_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("orange-pi-edge");
MODULE_DESCRIPTION("Minimal miscdevice driver for Linux driver learning");
MODULE_VERSION("1.0");

Makefile:

1
2
3
4
5
6
7
8
9
10
11
obj-m += misc_lab.o

KDIR ?= /lib/modules/$(shell uname -r)/build

all:
$(MAKE) -C $(KDIR) M=$(PWD) modules

clean:
$(MAKE) -C $(KDIR) M=$(PWD) clean

.PHONY: all clean

Linux驱动——MISC驱动
http://example.com/2026/08/04/Linux驱动——MISC驱动/
作者
Yu xin
发布于
2026年8月4日
许可协议