Linux 目录结构有很多有趣的功能。本月我将介绍 /dev 目录的一些有趣方面。在您继续阅读本文之前,我建议您(如果您尚未这样做)先阅读我之前的文章,一切皆文件 和 Linux 文件系统简介,这两篇文章都介绍了一些有趣的 Linux 文件系统概念。去吧——我会等你。
太棒了!欢迎回来。现在我们可以继续更详细地探索 /dev 目录。
设备文件
设备文件也称为 设备特殊文件。设备文件用于为操作系统和用户提供访问它们所代表的设备的接口。所有 Linux 设备文件都位于 /dev 目录中,该目录是根 (/) 文件系统的组成部分,因为这些设备文件在启动过程中必须可供操作系统使用。
关于这些设备文件,最重要的事情之一是要记住,它们绝对不是设备驱动程序。更准确地说,它们是设备驱动程序的门户。数据从应用程序或操作系统传递到设备文件,然后设备文件将其传递到设备驱动程序,然后设备驱动程序将其发送到物理设备。反向数据路径也使用,从物理设备通过设备驱动程序、设备文件,然后到应用程序或另一个设备。
让我们看一下典型命令的数据流,以可视化这一点。

图 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 中文件的列表,请注意文件上的日期和时间。所有这些文件都是在上次启动期间创建的。您可以使用 uptime 或 last 命令来验证这一点。在上面的设备列表中,所有这些文件都是在 11 月 7 日上午 7:06 创建的,这是我上次启动系统的时间。
当然,mknod 命令仍然可用,但新的 MAKEDEV 命令(是的,全部大写——在我看来,这与使用全部小写命令名称的 Linux 哲学相反)为创建设备文件提供了更简单的界面,以防需要这样做。MAKEDEV 命令在当前版本的 Fedora 或 CentOS 7 中默认未安装;它安装在 CentOS 6 中。您可以使用 YUM 或 DNF 安装 MAKEDEV 软件包。
结论
有趣的是,我已经很久没有需要创建设备文件了。但是,就在最近,我遇到了一个有趣的情况,我通常使用的一个设备文件没有被创建,我不得不创建它。从那时起,该设备没有任何问题。因此,仍然可能发生由设备文件丢失引起的情况,并且知道如何处理它可能很重要。
我没有介绍您可能遇到的无数不同类型的设备文件中的许多类型。这些信息在引用的资源中提供了大量的详细信息。我希望我已经让您对这些文件如何工作以及允许您自行探索更多的工具有了基本的了解。
资源
- 一切皆文件,David Both,Opensource.com
- Linux 文件系统简介,David Both,Opensource.com
- 文件系统层次结构,Linux 文档项目
- 设备文件,维基百科
- Linux 分配设备,Kernel.org
10 条评论