1.3 设备分类与识别:LED 为什么是字符设备


1.3 设备分类与识别:LED 为什么是字符设备

本节摘要:内核按访问模式把设备分为字符、块、网络三大类,每类对应一套驱动框架与接口约定;设备的"身份"则由设备号、设备树节点、总线枚举等机制确立。本节建立分类与识别的双视角,为后续各章选择正确的驱动框架打底。

先给一个可以直接背下来的判断标准:看设备按什么节奏交数据。按字节流、顺序读写的设备是字符设备,串口、LED、按键、传感器都属此类;按固定大小扇区、可随机寻址的设备是块设备,磁盘、闪存属此类;不经过文件系统、直接收发网络帧的设备是网络设备,网卡属此类。同一块硬件,访问方式不同,归类就不同——这是内核"以接口为中心"设计观的体现。

三类设备,三套规矩

分类不是贴标签,每类设备背后是一整套框架代码:框架替驱动做掉通用工作,驱动只填差异部分。

  • 字符设备:最古老也最自由的一类。驱动注册设备号与 file_operations,用户态通过读写、控制等文件接口访问。没有缓冲策略、没有请求合并,一切由驱动自己决定——LED 用它,串口用它,第 2 章整个围绕它展开。
  • 块设备:面向存储。用户对块设备的访问几乎都经过文件系统与页缓存,内核块层提供请求队列、合并、调度,驱动只需处理"一个个块请求"。第 5 章第一节展开。
  • 网络设备:不走文件接口。网卡驱动向网络协议栈注册收发接口,数据以套接字缓冲区的形态流转,接口状态变化通过专门的回调通知协议栈。第 5 章第二节展开。

三类设备驱动框架对照

三类设备驱动框架对照

判断练习:一块接在 I2C 总线上的温度传感器——按字节流读寄存器,字符设备;一块 eMMC 闪存——系统按扇区读写,块设备;蓝牙模块虽走串口,但对外提供网络接口,归网络设备。硬件形态相似,归类可以完全不同,先定类别,再选框架,是驱动开发的第一个技术决策。

⚠️ 常见坑:把"看起来像存储的设备"一律当块设备。一个提供 FIFO 读口的传感器,哪怕内部有存储,也该按字符设备写——分类的依据是接口行为,不是内部构造。

设备的身份从哪里来

分类解决"用哪套框架",识别解决"这台设备是谁"。内核世界里,设备身份由几种机制确立,各有各的舞台。

设备号是字符与块设备的身份证。每个这类设备拥有一个主设备号和一个次设备号:主号指明"由哪个驱动负责",次号让同一个驱动区分旗下多台设备。设备文件在磁盘上的索引节点里记录着这对号码——这就是 1.1 节里 VFS 能按图索骥的原因。查看当前系统的设备号清单:

$ cat /proc/devices Character devices: 1 mem 4 ttyS 5 /dev/tty 89 i2c 248 led Block devices: 1 ramdisk 259 blkext

主设备号 248 分配给了 LED 驱动——这个数字是动态分配的,重启后可能变化,这正是 2.4 节要引入 udev 的伏笔。

可枚举总线自带户口制度。USB、PCIe 这类总线有标准的枚举协议:控制器逐个询问总线上的每个槽位,设备自报厂商号、设备号、类别码。驱动用一张匹配表登记"我认得这些号码",总线枚举到匹配设备时自动调用驱动的 probe。这类设备天生"即插即用",不需要外部描述。

不可枚举的板载设备靠设备树描述。开发板上焊死的 GPIO、I2C 控制器、内存映射的外设,谁也不会"自报家门",必须由一块描述文件说明板上有谁、接在哪、用什么资源——这就是设备树,嵌入式世界最重要的识别机制,第 4 章用一整节的篇幅实战它。

桌面与服务器平台用 ACPI。同样的诉求在 x86 平台由 ACPI 表承担:固件向操作系统描述主板上的设备与电源管理方法。思路与设备树同源——硬件描述从驱动代码里搬出去,变成数据

从"硬编码"到"数据描述"的演进

早期内核的做法是把板级信息写死在源码里:每块新板子 fork 一份内核目录,改一堆地址常量。板子种类一多,维护就成了灾难。设备树与 ACPI 的出现终结了这个局面:驱动只描述"我会操作这类硬件",板级信息由数据文件提供,内核启动时把两者撮合到一起。这个演进影响深远——它让一份驱动可以服务千百块不同的板子,也是"驱动与设备分离"设计观的起点,第 4 章的平台总线模型由此展开。

用系统工具验证分类

分类不是纸上谈兵,运行中的系统随时可以验证。三件工具值得现在就熟悉起来:设备清单命令列出当前所有块设备与挂载点;设备号清单文件展示各类设备已注册的主号;输入设备清单文件(平台自带的事件设备目录)则把键盘鼠标这类输入设备另列一册——同一个系统里,字符、块、网络设备按各自户籍体系管理,这本身就是分类落地的证据

$ ls /sys/class block input net tty ... # 每类设备一个类别目录 $ ls /sys/class/net eth0 lo wlan0 # 网络设备:不走字符块两套体系

看类别目录是认识一台陌生系统的快捷方式:存储类目录下每个子目录是一块盘,网络类目录下每个子目录是一张网卡,输入类目录收录所有事件设备。第 2 章 2.4 节的 udev 规则、第 4 章的设备模型,全都建立在这套目录体系上——分类不只是驱动作者的事,它是整个系统管理硬件的索引结构

一道分类思考题

一块带 USB 接口的闪存盘插上系统,内核会为它创建哪些设备对象?拆开看:USB 总线枚举出设备(总线层身份);其上是大容量存储类驱动接管(协议层);再往下分出 SCSI 层的块设备(数据层是块设备);最后还有对应的设备文件(字符形态的访问入口)。同一物理设备在不同抽象层呈现不同身份,这正是"分类看接口行为"的最好注脚——问"它是什么设备"之前,先问"在哪一层问这个问题"。

常见追问

追问一:网络设备为什么在前面的设备号清单里查不到,也没有对应文件? 因为网络设备不走"文件接口"这条契约。设备号与设备文件是为"打开、读写、关闭"这套文件操作服务的;网卡的收发由协议栈直接调度,接口名直接进网络管理工具,不需要文件入口。这也解释了为什么早期有人尝试为网卡创建文件入口、后来又被废弃——为一套不同的接口硬套另一套外壳,只会两头别扭。分类口诀里"不走文件走协议栈"那一句,落到用户可见的层面就是这个现象。

追问二:主设备号是动态分配的,两个驱动会不会撞号?内核怎么管? 设备号由内核统一登记:分配时查询在册清单,已被占用的区间不会二次发放;驱动申请时可以指定独占区间,也可以完全委托动态分配。真正的麻烦不在分配阶段而在引用阶段——用户态脚本把设备号写死,重启后号变了,脚本就失灵。这个坑在 2.4 节会用 udev 的动态规则正式填平,这里只需建立印象:号码漂移不是内核的缺陷,而是"身份信息该由谁维护"的设计问题。同类的"写死一个会变的东西"陷阱,后面还会以中断号、内存地址的形态反复出现——识别它们的共同特征,比记住每个特例更有用。

本节要点回顾

  • 分类看访问节奏:字节流归字符、扇区归块、网络帧归网卡;类别决定框架。
  • 设备号是身份证:主号找驱动,次号分设备;动态分配的号码会漂移。
  • 识别机制分三路:可枚举总线自报家门,板载设备靠设备树,x86 平台靠 ACPI。
  • 硬件描述数据化是现代驱动模型的基石,也是第 4 章的主菜。

第 1 章至此收束:旅程走通了,边界看清了,设备也归了类。下一章开工——亲手写出那个让 LED 亮起来的字符设备驱动。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U