在 Linux 中管理设备

探索 /dev 目录如何让您直接访问 Linux 中的设备。
614 位读者喜欢这篇文章。
Penguins gathered together in the Artic

Opensource.com

Linux 目录结构有很多有趣的特性。本月我将介绍 /dev 目录的一些引人入胜的方面。在继续阅读本文之前,我建议您(如果您尚未这样做)先阅读我之前的文章,一切皆文件,以及 Linux 文件系统简介,这两篇文章都介绍了一些有趣的 Linux 文件系统概念。请先阅读——我会等你的。

太棒了!欢迎回来。现在我们可以继续更详细地探索 /dev 目录了。

设备文件

设备文件也称为 设备 特殊文件。设备文件用于为操作系统和用户提供访问它们所代表的设备的接口。所有 Linux 设备文件都位于 /dev 目录中,该目录是根 (/) 文件系统的组成部分,因为这些设备文件必须在启动过程中可供操作系统使用。

关于这些设备文件,最重要的事情之一是要记住,它们绝对不是设备驱动程序。更准确地说,它们是设备驱动程序的门户。数据从应用程序或操作系统传递到设备文件,然后设备文件将其传递给设备驱动程序,设备驱动程序再将其发送到物理设备。反向数据路径也使用,从物理设备通过设备驱动程序、设备文件,然后到应用程序或另一个设备。

让我们看一下典型命令的数据流,以便将其可视化。

dboth-dev-dir_0.png

图 1:典型命令的简单数据流。

在上面的图 1 中,显示了一个常用命令的简化数据流。从 GUI 终端模拟器(如 Konsole 或 xterm)发出 cat /etc/resolv.conf 命令会导致从磁盘读取 resolv.conf 文件,磁盘设备驱动程序处理设备特定功能,例如在硬盘驱动器上查找文件并读取它。数据通过设备文件传递,然后从命令传递到伪终端 6 的设备文件和设备驱动程序,并在终端会话中显示。

当然,cat 命令的输出可以重定向到一个文件,方法如下:cat /etc/resolv.conf > /etc/resolv.bak,以便创建文件的备份。在这种情况下,图 1 左侧的数据流将保持不变,而右侧的数据流将通过 /dev/sda2 设备文件、硬盘驱动器设备驱动程序,然后到达硬盘驱动器本身。

这些设备文件使使用标准流 (STD/IO) 和重定向来访问 Linux 或 Unix 计算机上的任何和所有设备变得非常容易。只需将数据流定向到设备文件,即可将数据发送到该设备。

分类

设备文件至少可以按两种方式分类。第一种也是最常用的分类是设备通常关联的数据流。例如,tty(电传打字机)和串行设备被认为是基于字符的,因为数据流一次传输和处理一个字符或字节。块类型设备(如硬盘驱动器)以块为单位传输数据,通常是 256 字节的倍数。

如果您还没有这样做,请继续,并以非 root 用户身份在终端会话中,将当前工作目录 (PWD) 更改为 /dev 并显示长列表。这会显示设备文件的列表,其中包含它们的文件权限以及它们的主设备号和次设备号。例如,以下设备文件只是我的 Fedora 24 工作站上的 /dev/目录中的一部分。它们代表磁盘和 tty 类型设备。请注意输出中每行最左边的字符。带有“b”的那些是块类型设备,而以“c”开头的那些是字符设备。

brw-rw----   1 root disk        8,   0 Nov  7 07:06 sda 
brw-rw----   1 root disk        8,   1 Nov  7 07:06 sda1 
brw-rw----   1 root disk        8,  16 Nov  7 07:06 sdb 
brw-rw----   1 root disk        8,  17 Nov  7 07:06 sdb1 
brw-rw----   1 root disk        8,  18 Nov  7 07:06 sdb2
crw--w----   1 root tty         4,   0 Nov  7 07:06 tty0 
crw--w----   1 root tty         4,   1 Nov  7 07:07 tty1 
crw--w----   1 root tty         4,  10 Nov  7 07:06 tty10 
crw--w----   1 root tty         4,  11 Nov  7 07:06 tty11

更详细和明确地识别设备文件的方法是使用设备主设备号和次设备号。磁盘设备的主设备号为 8,这将其指定为 SCSI 块设备。请注意,所有 PATA 和 SATA 硬盘驱动器都由 SCSI 子系统管理,因为旧的 ATA 子系统多年前就被认为由于其代码质量差而无法维护。因此,以前被指定为“hd[a-z]”的硬盘驱动器现在被称为“sd[a-z]”。

您可能可以从上面显示的小样本中推断出磁盘驱动器次设备号的模式。次设备号 0、16、32 等直到 240 是整个磁盘编号。因此,主设备号/次设备号 8/16 代表整个磁盘 /dev/sdb,而 8/17 是第一个分区 /dev/sdb1 的设备文件。数字 8/34 将是 /dev/sdc2。

上面列表中的 tty 设备文件的编号稍微简单一些,从 tty0 到 tty63。

Kernel.org 上的 Linux 分配设备 文件是设备类型以及主设备号和次设备号分配的官方注册表。它可以帮助您了解所有当前定义的设备的主设备号/次设备号。

设备文件的乐趣

现在让我们花几分钟时间进行几个有趣的实验,这将说明 Linux 设备文件的强大功能和灵活性。大多数 Linux 发行版都有多个虚拟控制台,1 到 7,可用于登录到具有 shell 界面的本地控制台会话。可以使用组合键 Ctrl-Alt-F1(用于控制台 1)、Ctrl-Alt-F2(用于控制台 2)等来访问这些控制台。

按 Ctrl-Alt-F2 切换到控制台 2。在某些发行版上,登录信息包括与此控制台关联的 tty 设备,但许多发行版不包括。它应该是 tty2,因为您在控制台 2 中。

以非 root 用户身份登录。然后,您可以使用 who am i 命令(是的,就像那样,带有空格)来确定哪个 tty 设备连接到此控制台。

在我们实际执行此实验之前,请查看 /dev 中 tty2 和 tty3 设备的列表。

ls -l /dev/tty[23]

将定义大量 tty 设备,但我们不关心它们中的大多数,只关心 tty2 和 tty3 设备。作为设备文件,它们没有什么特别之处;它们只是字符类型设备。我们将使用这些设备进行此实验。tty2 设备连接到虚拟控制台 2,tty3 设备连接到虚拟控制台 3。

按 Ctrl-Alt-F3 切换到控制台 3。再次以相同的非 root 用户身份登录。现在在控制台 3 上输入以下命令。

echo "Hello world" > /dev/tty2

按 Ctrl-Alt-F2 返回到控制台 2。字符串“Hello world”(不带引号)显示在控制台 2 中。

此实验也可以在 GUI 桌面上的终端模拟器上执行。桌面上的终端会话使用 /dev 树中的伪终端设备,例如 /dev/pts/1。使用 Konsole 或 Xterm 打开两个终端会话。确定它们连接到哪个伪终端,并使用一个终端向另一个终端发送消息。

现在继续实验,使用 cat 命令在不同的终端上显示 /etc/fstab 文件。

另一个有趣的实验是使用 cat 命令将文件直接打印到打印机。假设您的打印机设备是 /dev/usb/lp0,并且您的打印机可以直接打印 PDF 文件,则以下命令将在您的打印机上打印 PDF 文件 test.pdf。

cat test.pdf > /dev/usb/lp0

/dev 目录包含一些非常有趣的设备文件,这些文件是硬件的门户,而硬件通常不会被认为是像硬盘驱动器或显示器这样的设备。例如,系统内存 RAM 通常不被视为“设备”,但 /dev/mem 是实现直接访问内存的门户。以下示例产生了一些有趣的结果。

dd if=/dev/mem bs=2048 count=100

上面的 dd 命令比简单地使用 cat 命令转储系统的所有内存提供了更多的控制。它提供了指定从 /dev/mem 读取多少数据的能力,并且还允许我指定从内存中开始读取数据的点。虽然读取了一些内存,但内核响应了以下错误,我在 /var/log/messages 中找到了该错误。

Nov 14 14:37:31 david kernel: usercopy: kernel memory exposure attempt detected from ffff9f78c0010000 (dma-kmalloc-512) (2048 bytes)

此错误意味着内核正在尽职尽责地保护属于其他进程的内存,这正是它应该工作的方式。因此,尽管您可以使用 /dev/mem 显示 RAM 内存中存储的数据,但对大多数内存空间的访问受到保护,并将导致错误。只有内核内存管理器分配给运行 dd 命令的 BASH shell 的虚拟内存才应该可以在不导致错误的情况下访问。抱歉,除非您找到可利用的漏洞,否则您无法窥探不属于您的内存。

/dev 中还有一些其他非常有趣的设备文件。设备文件 null、zero、random 和 urandom 与任何物理设备都不关联。

例如,空设备 /dev/null 可以用作从 shell 命令或程序重定向输出的目标,以便它们不会显示在终端上。我经常在我的 BASH 脚本中使用它,以防止用户看到可能让他们感到困惑的输出。/dev/null 设备可用于生成空字符字符串。使用如下所示的 dd 命令查看来自 /dev/null 设备文件的一些输出。

# dd if=/dev/null  bs=512 count=500 | od -c     
0+0 records in
0+0 records out
0 bytes copied, 1.5885e-05 s, 0.0 kB/s
0000000

请注意,实际上没有可见的输出,因为空字符什么也不是。请注意字节计数。

/dev/random 和 /dev/urandom 设备也非常有趣。顾名思义,它们都产生随机输出——不仅是数字,而且是任何和所有的字节组合。/dev/urandom 设备产生确定性随机输出,并且速度非常快。这意味着输出由算法确定,并使用种子字符串作为起点。因此,如果原始种子已知,黑客有可能(尽管非常困难)重现输出。使用命令 cat /dev/urandom 查看典型输出。您可以使用 Ctrl-c 退出。

/dev/random 设备文件产生非确定性随机输出,但它的输出速度较慢。此输出不是由依赖于前一个数字的算法确定的,而是响应于按键和鼠标移动而生成的。这种方法使得复制特定的随机数序列更加困难。使用 cat 命令查看来自 /dev/random 设备文件的一些输出。尝试移动鼠标以查看它如何影响输出。

顾名思义,/dev/zero 设备文件产生永无止境的零字符串作为输出。请注意,这些是八进制零,而不是 ASCII 字符零 (0)。使用如下所示的 dd 命令查看来自 /dev/zero 设备文件的一些输出。

# dd if=/dev/zero  bs=512 count=500 | od -c
0000000  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0
*
500+0 records in
500+0 records out
256000 bytes (256 kB, 250 KiB) copied, 0.00126996 s, 202 MB/s
0764000

请注意,此命令的字节计数为非零。

创建设备文件

过去,/dev 中的设备文件都是在安装时创建的,导致一个目录中充满了几乎所有可能的设备文件,即使大多数永远不会被使用。在不太可能需要新的设备文件或意外删除一个设备文件并且需要重新创建它的情况下,mknod 程序可用于手动创建设备文件。您只需要知道设备主设备号和次设备号。

CentOS 和 RHEL 6 和 7,以及至少可以追溯到 Fedora 15 的所有 Fedora 版本,都使用较新的方法创建设备文件。所有设备文件都在启动时创建。此功能之所以成为可能,是因为 udev 设备管理器会检测设备的添加和删除。这允许在主机启动并运行时实现真正的动态即插即用功能。它还在启动时执行相同的任务,在启动过程的早期检测系统上安装的所有设备。Linux.com 上有一个关于 udev 的介绍 的良好描述。

回到您在 /dev 中文件的列表,请注意文件上的日期和时间。所有这些文件都是在上次启动期间创建的。您可以使用 uptimelast 命令来验证这一点。在上面的设备列表中,所有这些文件都是在 11 月 7 日上午 7:06 创建的,这是我上次启动系统的时间。

当然,mknod 命令仍然可用,但新的 MAKEDEV 命令(是的,全部大写——在我看来,这与使用全部小写命令名称的 Linux 哲学背道而驰)为创建设备文件提供了更简单的界面,以防需要时使用。MAKEDEV 命令在当前版本的 Fedora 或 CentOS 7 中默认未安装;它安装在 CentOS 6 中。您可以使用 YUM 或 DNF 安装 MAKEDEV 包。

结论

有趣的是,我已经很长时间不需要创建设备文件了。但是,就在最近,我遇到了一个有趣的情况,我通常使用的设备文件之一没有创建,我确实必须创建它。从那以后,该设备没有任何问题。因此,由缺少设备文件引起的情况仍然可能发生,并且知道如何处理它可能很重要。

我没有介绍您可能遇到的无数种不同类型的设备文件中的许多类型。有关信息在引用的资源中提供了大量详细信息。我希望我已经让您对这些文件如何工作以及允许您自行探索更多内容的工具有了基本的了解。

资源

David Both
David Both 是开源软件和 GNU/Linux 的倡导者、培训师、作家和演讲者。自 1996 年以来,他一直从事 Linux 和开源软件工作,自 1969 年以来一直从事计算机工作。他是“系统管理员 Linux 哲学”的坚定支持者和传播者。

10 条评论

很棒的文章,David。

这些都是宝藏。
谢谢 @David Both

我认为声明“cat /ets/resolv.conf”是印刷错误,应该是“/etc/resolv.conf”。

你是对的。我已经修复了。谢谢!

回复 作者:Tim Faulkner (未验证)

既然您已经介绍了文件和设备,我希望您能讨论一下当连接存储设备(例如,U 盘、外部驱动器、媒体卡等)到工作站时发生的“设备舞蹈”。我推测,当连接其他设备(例如,指点设备、摄像头、音频播放器、网络适配器等)时,也会发生类似的“设备舞蹈”。

也许您可以更具体地描述您所说的“设备舞蹈”。此外,了解您的发行版和版本会有所帮助。有了这些信息,我会尽力而为。

回复 作者:Dan Saint-Andre (未验证)

“设备舞蹈”是一个很好的说法。我使用的是 Kubuntu 15.10,虽然闪存盘/U 盘连接得相当好,但 Android 设备/手机和相机使用 MTP 协议,可能需要多次插拔才能进行读/写文件。

哇!非常有用的信息。真的很喜欢你的文章,David。

嗨,
这是一篇非常好的文章,信息和概念都很强大,感谢分享
再见

Ernesto Escobedo
PD Kernel.org 链接已损坏

一颗宝石。
谢谢 David

Creative Commons License本作品根据 Creative Commons Attribution-Share Alike 4.0 International License 获得许可。
© . All rights reserved.