ani*_*l07 8 drivers linux-kernel
我刚刚进入 Linux 驱动程序开发,我有一个概念性问题,我认为这也将帮助其他新手进入内核开发。
我正在阅读 Linux 设备驱动程序这本书,并且我已经完成了 Ch。3 本书。到目前为止,我已经看到通过向文件夹中的文件发出open
,close
和其他命令/dev
,用户空间可以访问内核函数。
另一种共享控制的方法是通过 中的文件/sys
,从sys
文件中读取或写入可以与驱动程序通信。
我想知道每种方法的用例是什么?它们是完成同一任务的两种方法吗?一个比另一个有任何限制吗?有人可以分享一个可能比另一个有用的实际例子吗?
我在这里阅读了其他问题,他们解释了dev
和sys
。虽然这很有帮助,但我想更深入地了解两者的区别和应该如何使用。
非常粗略:
/dev
包含设备节点,这在早期的 Unix 系统中是与内核交互的唯一方式。有两种类型,块设备和字符设备。相应的 API 适用于允许基于块的 I/O(某种磁盘)或基于字符的 I/O(例如串行端口)的东西。
/sys
(和/proc
) 是后来添加的,可能受到Plan 9 的启发 OS 的。它们提供完整的目录子树,这些子树中的文件条目包含描述内核模块在读取时或写入时设置内部状态时的内部状态的文本。
所以一个典型的应用是:
您想为某种存储设备编写内核驱动程序吗?使用/dev
节点访问设备本身,和/sys
(或/proc
)条目来微调存储的访问方式。
的覆盖范围/sys
在第 14 章“Linux 设备模型”中。它提供了更多示例代码供您使用。但我猜这本书是一种更代码驱动的方法,询问设计原则是什么样的很有用。
第一个答案是,你不选择自己。当您为某种类型的设备编写驱动程序时,内核已经有许多相同类型设备的示例。您可以使用与现有设备等效的代码模式。(最新的文档是代码本身。许多内核接口没有完整或最新的文档,抱歉!)
这是编写设备驱动程序的一个主要原因。您提供了程序可以使用的一致接口,而不必为每个不同的设备编写不同的详细信息。
这会覆盖任何更一般的建议。如果 Linux 的一个子系统(即一种设备)使用了一种看似“错误”但始终如一的方法,那么您在为该子系统编写驱动程序时也应该保持一致和“错误”。
/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
规则想要测试这些属性。
[**] 除了/dev/pts/0
等等没有在 中列出/sys
,因为/dev/pts/0
实际上是由一个单独的文件系统“devpts”实现的。请忽略/dev/pts/0
作为一个非常非常特殊的情况。有一个答案是我得出的结论,但我真的不认为它对我刚才所说的内容有任何补充。它在这里:当我们打开任何终端时,是否总是使用 TTY?.