Posts Linux 设备
Post
Cancel

Linux 设备

1. 设备的本质

在 Linux 里,设备就是内核暴露给用户空间的接口,让程序可以操作硬件或内核功能。

  • 不一定是硬件:例如 /dev/null/dev/random/dev/zero 都是虚拟设备
  • 内核通过 驱动程序 控制设备行为
  • 核心接口就是系统调用:open / read / write / ioctl / mmap

2. 设备分类

Linux 设备主要分三类:

类别内核驱动类型用户空间访问/dev 节点
块设备 Blockblock driverread/write/ioctl/dev/sda、/dev/loop0
字符设备 Charchar driverread/write/ioctl/dev/ttyS0、/dev/null
网络设备 Netnet driversocket API(send/recv)没有 /dev 文件

重点:块/字符设备有 /dev 节点,网络设备靠 socket API。


3. /dev 节点

每个 /dev 文件对应内核中的设备对象:

  • major number:告诉内核用哪个驱动处理
  • minor number:同一驱动下的不同实例

用户态通过文件操作访问设备,内核驱动处理操作:

1
2
int fd = open("/dev/ttyS0", O_RDWR);
write(fd, buf, size);

4. 举例

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
// ----------------- 块设备 Block -----------------
// /dev/sda 是硬盘,块设备,可以随机读写
#include <fcntl.h>
#include <unistd.h>

int fd = open("/dev/sda", O_RDONLY);
char buf[512];
lseek(fd, 1024*512, SEEK_SET);  // 跳到第1024块
read(fd, buf, 512);             // 读取一块数据
close(fd);

// ----------------- 字符设备 Char -----------------
// /dev/ttyS0 是串口,字符设备,顺序流式访问
#include <fcntl.h>
#include <unistd.h>

int fd = open("/dev/ttyS0", O_RDWR);
char buf[100];
int n = read(fd, buf, sizeof(buf));   // 读取串口数据
write(fd, "hello\n", 6);              // 向串口发送数据
close(fd);

// /dev/null 是字符设备,但丢弃数据
int fd = open("/dev/null", O_WRONLY);
write(fd, "any data", 8);  // 写进去立即丢弃
close(fd);

// ----------------- 网络设备 Net -----------------
// eth0 是网卡,使用 socket API 访问,没有 /dev 节点
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int sock = socket(AF_INET, SOCK_DGRAM, 0);  // UDP 套接字
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(12345);
inet_pton(AF_INET, "192.168.1.10", &addr.sin_addr);
sendto(sock, "hello", 5, 0, (struct sockaddr*)&addr, sizeof(addr));
close(sock);

5. 设备号

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
/*
 * =========================
 * Linux 设备号(major/minor)详解
 * =========================
 *
 * 1️⃣ 设备节点对应内核驱动
 *
 * 在 Linux 中,/dev 下的文件只是一个设备节点,并不是真正的设备。
 * 当你操作 /dev/sda、/dev/ttyS0 时,内核根据这个节点的编号
 * 把请求路由给对应的驱动。
 *
 * 例如:
 *   int fd = open("/dev/sda", O_RDONLY);
 * 内核会根据 major/minor 找到对应的驱动,并将请求映射到硬件。
 *
 * -------------------------
 *
 * 2️⃣ major 和 minor 的定义
 *
 * 每个设备节点有两个数字:
 *
 *   major number : 指明使用哪个内核驱动处理操作。
 *                  例如,major=8 表示 Linux 的 SCSI 硬盘驱动。
 *   minor number : 同一驱动下不同实例的标识。
 *                  例如,8/0 是 /dev/sda,8/1 是 /dev/sdb。
 *
 * 查看设备号示例:
 *
 *   $ ls -l /dev/sda
 *   brw-rw---- 1 root disk 8, 0 Oct 9 11:00 /dev/sda
 *
 * 解析:
 *   'b' → 块设备
 *   8   → major
 *   0   → minor
 *
 * 对字符设备同理:
 *
 *   $ ls -l /dev/ttyS0
 *   crw-rw---- 1 root dialout 4, 64 Oct 9 11:00 /dev/ttyS0
 *
 * 解析:
 *   'c' → 字符设备
 *   4   → major(对应 tty 驱动)
 *   64  → minor(对应具体串口 ttyS0)
 *
 * -------------------------
 *
 * 3️⃣ 系统调用流程
 *
 * 当程序调用 read/write/open/close 等系统调用时:
 *
 *   1. 内核通过 major 找到对应驱动
 *   2. 内核通过 minor 选择具体实例
 *   3. 驱动执行对应操作,将请求映射到真实硬件
 *
 * -------------------------
 *
 * 4️⃣ 拓展知识
 *
 * - 杂项设备(misc device)通常用 major=10,不同 minor 表示不同模块,
 *   例如 /dev/kni。
 * - 网络设备没有 /dev 节点,没有 major/minor,通过 socket API 访问。
 *
 */

6. 杂项设备

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
/*
 * =========================
 * Linux 杂项设备(misc device)解析
 * =========================
 *
 * 1️⃣ 背景问题
 *
 * 在 Linux 中,每个设备类型都需要一个 major 号,驱动注册时通过 major 区分哪个驱动处理请求:
 *
 *   register_chrdev(major, "name", &fops);
 *   register_blkdev(major, "name");
 *
 * 早期做法是每个驱动都分配独立 major 号。
 * 
 * 问题:
 *   - major 号有限(0~255 对于字符设备)
 *   - 随着设备和模块数量增多,major 很快不够
 *   - 为每个小功能设备分配 major 太浪费,也很麻烦
 *
 * -------------------------
 *
 * 2️⃣ 杂项设备(misc device)的设计目的
 *
 * 核心思想:
 *   - 将小型、使用频率低或实验性设备归类到一个统一类别
 *   - 统一使用 major=10,用 minor 区分具体设备
 *
 * 优点:
 *   1. 节省 major 号:所有杂项设备共享 major=10,minor 区分功能
 *   2. 模块化方便:每个 misc 设备可以独立注册自己的驱动和 file_operations
 *   3. 减少冲突:统一 major + minor 避免和其他设备冲突
 *   4. 适合小型或虚拟设备:小功能模块不需要独立 major
 *
 * -------------------------
 *
 * 3️⃣ 类比理解
 *
 * 可以把 Linux 设备号比作邮政系统:
 *
 *   类别         | major       | minor           | 类比
 *   ------------ | ----------- | --------------- | ------------------------
 *   普通块设备   | 独立 major | minor 标识实例  | 大型建筑,每栋楼有自己编号
 *   杂项设备     | major=10   | minor 区分设备  | 同一个小区,minor 是房间号,每个房间独立管理
 *
 * -------------------------
 *
 * 4️⃣ /dev、设备节点和杂项设备
 *
 * - /dev 是用户空间访问设备的入口
 * - 大部分文件是设备节点(block/char device)
 * - 用户程序通过 open/read/write/ioctl 操作 /dev 下文件
 * - 杂项设备在 /dev 下也有节点,例如 /dev/kni、/dev/rtc
 *
 * 工作流程:
 *   用户程序 open("/dev/kni")
 *       |
 *       v
 *   内核检查 major=10,minor=对应设备
 *       |
 *       v
 *   调用 misc 驱动的 file_operations
 *       |
 *       v
 *   驱动执行具体操作
 *
 * /dev 提供接口入口,并不直接实现功能
 *
 * -------------------------
 *
 * 5️⃣ /proc 目录作用
 *
 * - /proc 是内核信息伪文件系统,提供内核数据结构和状态接口
 * - /proc/misc 列出所有注册的杂项设备
 *   格式:minor 设备名
 *
 * 示例:
 *   1   psaux
 *   4   rtc
 * 256   kni
 *
 * /proc 目录不提供硬件访问,只显示内核状态
 *
 * -------------------------
 *
 * 6️⃣ /dev 和 /proc 与杂项设备关系
 *
 * 位置       | 内容                        | 作用
 * --------- | -------------------------- | -----------------------------------------
 * /dev      | 设备节点(字符设备 c)       | 用户空间接口,通过 major/minor 调用驱动
 * /proc/misc| misc 设备列表(minor+名字)  | 内核维护的注册信息,查看状态
 * 驱动      | 内核模块                     | 处理实际系统调用,执行功能
 *
 * 关系示意:
 *
 *   用户程序
 *      |
 *      v
 *   /dev/kni  <-- major=10, minor=256 --> 内核 misc 驱动
 *                                            ^
 *                                            |
 *                                     /proc/misc(显示注册信息)
 *
 * 💡 总结:
 *   - /dev:用户空间访问通道
 *   - /proc/misc:内核维护的 misc 设备注册列表,用于查看设备和 minor
 *   - 杂项设备本身:major=10,minor 区分具体设备,每个设备有独立驱动
 *   - 工作流程:用户访问 /dev -> 内核通过 major/minor 找到驱动 -> 驱动执行操作 -> /proc/misc 显示状态
 *
 * -------------------------
 *
 * 7️⃣ 杂项设备的主要目的
 *
 * - 节省 major 号资源
 * - 方便管理小型或实验性设备
 * - 避免驱动冲突
 * - 支持动态添加和删除设备
 *
 * 核心理解:
 *   major=10 -> 表示 misc 类别
 *   minor -> 唯一标识设备实例
 *   每个 misc 设备都有独立驱动逻辑
 */
This post is licensed under CC BY 4.0 by the author.

Contents

Trending Tags