我刚刚开始 Linux 驱动程序开发,有一个概念性问题,我认为这也有助于其他新手进入内核开发。
我正在阅读《Linux 设备驱动程序》一书,并且已经完成了第 1 章。书的3.到目前为止,我已经看到通过向文件夹中的文件发出open
、close
和其他命令/dev
,用户空间可以访问内核功能。
共享控制的另一种方法是通过 中的文件/sys
,其中从文件读取或写入sys
可以与驱动程序通信。
我想知道每种方法的用例是什么?它们是完成同一任务的两种方法吗?一个人相对于另一个人有任何限制吗?有人可以分享一些实际例子,其中一个可能比另一个更有用吗?
我已经阅读了这里的其他问题,他们解释了dev
和sys
。虽然这很有帮助,但我想更深入地了解两者的区别和用途。
答案1
非常粗略地:
/dev
包含设备节点,在早期的 Unix 系统中,这是与内核交互的唯一方式。其中有两种类型,堵塞设备和特点设备。相应的 API 适合允许基于块的 I/O(某种磁盘)或基于字符的 I/O(例如串行端口)。
/sys
(和/proc
)是后来添加的,可能是受到计划9操作系统。它们提供完整的目录子树,这些子树中的文件条目包含描述内核模块读取时的内部状态或写入时设置内部状态的文本。
所以一个典型的应用是:
您想为某种存储设备编写内核驱动程序吗?使用/dev
节点来访问设备本身,和/sys
(或/proc
)条目来微调存储的访问方式。
答案2
/sys
第 14 章“Linux 设备模型”中对此进行了介绍。它提供了更多示例代码可供使用。但我认为这本书是一种更多由代码驱动的方法,询问设计原则是什么样子很有用。
何时使用 /dev 和 /sys 进行用户空间-内核通信?
第一个答案是,你不能选择自己。当您为某种类型的设备编写驱动程序时,内核已经拥有许多相同类型设备的示例。您可以使用与现有设备等效的代码模式。 (最新的文档是代码本身。许多内核接口没有完整或最新的文档,抱歉!)
这是编写设备驱动程序的主要原因。您提供程序可以使用的一致接口,而无需为每个不同的设备编写不同的细节。
这优先于任何更一般的建议。如果 Linux 的一个子系统(即一类设备)使用了一种看似“错误”的方法,但却始终如一地这样做,那么当您为该子系统编写驱动程序时,也应该保持一致且“错误”。
/dev
/dev/ 应该用于数据路径。网络设备除外,这些设备将在本书的不同部分中介绍。
/dev/ 特殊文件应该用于标准化 unix 操作:read()
, write()
, poll()
/ select()
, mmap()
.当ioctl()
s 在 UNIX 或 Linux 中得到有效标准化时,也是可取的。这几个系统调用(以及一些派生变体)几乎在所有情况下都会使用。开始熟悉它们:)。 ioctl()
此处是一个逃生舱口,可用于让您的驱动程序定义任意数量的其他操作。
/系统
sysfs 写入应该在较少数量的情况下用于配置参数。它们必须是纯文本格式,并且应该只包含单个值。[*] 您不会希望有太多不同的 sysfs 文件。您很快就会开始看到它们的局限性。
我们还可以说 sysfs 文件基本上应该读取一个变量(该变量可能是也可能不是可写的)。我认为阅读它们不应该引起任何硬件操作。我想你已经在沿着这些思路思考了。
sysfs 文件的一个优点是它们非常方便进行实验。您可以轻松地使用 shell 命令来列出、读取和写入它们。这也是一个危险:您必须注意不要发布处于实验状态的 sysfs 文件。一旦其他人开始依赖您的 sysfs 文件,如果您想以某种破坏用户脚本的方式进行更改,内核维护人员将非常不高兴。
遍历 sysfs(目录层次结构和符号链接)对于查看设备的组织方式也很有用。
此外,从概念上讲,监视 sysfs 中的变化是程序能够检测新设备(例如插入时)的方式。从技术上讲,这些事件不是通过文件系统本身传递的,但每个事件都指向特定的 sysfs 目录。
udev 守护进程监听这些事件;如果需要的话,普通程序应该倾向于依赖 udev/libudev。例如,当接收到事件时,udev 规则可能会读取或写入 sysfs 目录中的一些文件,例如更改新发现的设备上的设置。
查看示例
查看现有示例是一个非常好的主意。尽管您可能不应该只看一个示例,并假设它在其他地方都一样工作:-)。
正如 dirkt 所说,访问块存储设备/dev/sda
是/dev
.一些参数,例如暴露的最大物理 IO 大小/sys/class/block/sda/
- 请在子目录中查看queue
。
许多 sysfs 文件都记录在内核树中Documentation/ABI/*/sysfs-*
。例如:https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-block
请注意,“字符设备”并不是很具体。它基本上用于既不是块设备也不是网络设备的任何设备:-)。如果您必须实现某种全新类型的设备,您可能必须定义一种全新类型的字符设备,并承担定义一些新ioctl()
操作集的可怕工作。
您可能会开始查看/sys/class/
,以查看您自己的系统上定义的一些其他类型的设备。
/sys/
还包括设备列表/dev/
,但它是按设备编号而不是名称列出的。参见ls -l /sys/dev/char/
和ls -l /sys/dev/block/
。这有助于解释如何udev
管理/dev
:出现的每个设备都被列为.[**]/dev
中的对象/sys
[*] sysfsuevent
文件包含多个值,实际上是一个核心功能。但是uevent文件不能用来改变里面的值。所以这只是说:不要看着uevent
并认为我的建议是错误的;您不应该自己定义这样的文件。设备驱动程序可以向其uevent
文件添加行;我思考一个很好的例子是,如果您有一些非常有用的识别属性,udev
规则想要测试这些属性。
[**] except/dev/pts/0
等没有在 中列出/sys
,因为/dev/pts/0
实际上是由单独的文件系统“devpts”实现的。请忽略/dev/pts/0
作为一个非常非常特殊的情况。我得出的结论是有一个答案,但我真的不认为它对我刚才所说的内容有任何补充。它在这里:当我们打开任何终端时,总是会使用 TTY 吗?。