回环设备(loop-back devices) ¶
回环设备( 'loopback device')允许用户以一个普通磁盘文件虚拟一个块设备。设想一个磁盘设备,对它的所有读写操作都将被重定向到读写一个名为 disk-image 的普通文件而非操作实际磁盘或分区的轨道和扇区。(当然,disk-image 必须存在于一个实际的磁盘上,而这个磁盘必须比虚拟的磁盘容量更大。)回环设备允许你这样使用一个普通文件。
回环设备以 /dev/loop0、/dev/loop1 等命名。每个设备可虚拟一个块设备。注意只有超级用户才有权限设置回环设备。
回环设备的使用与其它任何块设备相同。特别是,你可以在这个设备上创建文件系统并像普通的磁盘一样将它挂载在系统中。这样的一个将全部内容保存在一个普通文件中的文件系统,被称为虚拟文件系统(virtual file system)(译者注:这个用法并不常见。VFS 通常另有所指,如指代 Linux 内核中有关文件系统抽象的代码层次等)。
可以通过下列步骤创建一个虚拟文件系统并通过回环设备挂载:
创建一个用于承载虚拟文件系统的空文件。这个文件的大小将成为挂载后文件系统的大小。
创建指定大小文件的简单方法是通过 dd 命令。这个命令以块为单位(通常为 512 字节)从一个文件向另一个文件复制数据。/dev/zero 文件则是一个很好的数据来源。
要建立一个 10 MB 大的名为 disk-image 的文件可以通过以下命令:
% dd if=/dev/zero of=/tmp/disk-image count=20480
20480+0 records in
20480+0 records out
% ls -l /tmp/disk-image
-rw-rw---- 1 root root 10485760 Mar 8 01:56 /tmp/disk-image
这个新建立的文件被填满了 0 字节。在挂载之前,必须在其上建立一个文件系统。这个过程会建立许多用于组织和存储文件的控制单元并构造根目录结构。
在这个磁盘映像之上可以构建任何类型的文件系统。以创建 ext2 文件系统为例(ext2 是 Linux 系统中最常见的文件系统),用 mke2fs 可以完成这个操作。因为这个命令通常是针对块设备进行操作,当对一个普通文件操作时它会要求确认:
% mke2fs -q /tmp/disk-image
mke2fs 1.18, 11-Nov-1999 for EXT2 FS 0.5b, 95/08/09
disk-image is not a block special device.
Proceed anyway? (y,n) y
这里 -q 参数用于省略输出有关新建立文件系统的概要信息。如果你想看到这些信息,则请省略这个参数。
现在 disk-image 文件包含了一个新建立的文件系统,正如一个被刚刚初始化完毕的 10 MB 大小的磁盘。
以一个环回设备挂载这个文件系统。方法是使用 mount 命令,指定磁盘文件为被挂载的设备。同时指定 loop=loopback-device 作为 -o 选项的参数,告诉 mount 命令使用哪个回环设备。
下面例子中的命令可用于挂载我们的 disk-image 文件系统。要记住的是只有超级用户可以使用环回设备。第一个命令将创建一个目录 /tmp/virtual-fs,这个目录将被用于挂载我们的文件系统。
% mkdir /tmp/virtual-fs
% mount -o loopback=/dev/loop0 /tmp/disk-image /tmp/virtual-fs
这时,这个设备应该已经被挂载,就如同一个普通的 10M 空间的磁盘一样。
% df -h /tmp/virtual-fs
Filesystem Size Used Avail Use% Mounted on
/tmp/disk-image 9.7M 13k 9.2M 0% /tmp/virtual-fs
你可以向任何其它磁盘一样使用这个设备:
% cd /tmp/virtual-fs
% echo 'Hello, world!' > test.txt
% ls -l
total 19
drwxr-xr-x 2 root root 12288 Mar 8 02:00 lost+found
-rw-rw---- 1 root root 14 Mar 8 02:12 test.txt
% cat test.txt
Hello, world!
请注意 lost+found 是一个由 mke2fs 自动建立的文件夹一旦文件系统被破坏,部分数据被回复但没有与任何文件关联起来,将被放置在这个文件夹中。。
结束使用后,卸载这个文件系统:
% cd /tmp
% umount /tmp/virtual-fs
你可以删除 disk-image,或者之后再次挂载并使用其中的文件。你甚至可以将这个文件复制到远程主机并在那里挂载、使用——文件系统的内容完全不会受到影响。
除了从新创建一个文件系统,还可以从一个现有的文件系统复制而得到一份映像。例如,可以通过普通的复制操作为一个 CD-ROM 创建一份映像。
如果系统中有一个 IDE 接口的 CD-ROM 驱动器,使用前面说过的设备名如 /dev/hda。如果 CD-ROM 是 SCSI 接口的话,设备文件名可能是 /dev/scd0 之类。系统中也可能包含一个符号链接 /dev/cdrom 指向实际的光驱。请参考 /etc/fstab (译者注: 手册)查看系统实际使用的光驱对应的设备。
接下来要做的仅仅是将这个设备复制为一个文件——得到的文件将是被存在硬盘上的、这个 CD-ROM 内容的完整映像。例如:
% cp /dev/cdrom /tmp/cdrom-image
取决于设备的速度和 CD-ROM 的容量,这个操作可能需要几分钟的时间。最终的文件将会相当大,它的体积与这个 CD-ROM 的容量相同。
这时,你可以在系统中挂载这个光盘而无须插入原始的 CD-ROM 盘片。要挂载在 /mnt/cdrom 目录下:
mount -o loopback=/dev/loop0 /tmp/cdrom-image /mnt/cdrom
因为映像位于磁盘上,这个文件系统的速度将远胜于直接挂载自实际光驱的时候。请注意多数光盘使用的文件系统类型都是 iso9660。
--------------------
特殊设备
Linux 同时提供了多个并不对应于硬件的设备文件。这些设备的主设备号都是 1;这个主设备号对应于 Linux 内核的内存设备而非任何设备驱动。
/dev/null
/dev/null 文件,null 设备,是一个经常被用到的设备。它有两个用途;通常而言,你至少熟悉其中一个用途:
Linux 会丢弃所有别写入到 /dev/null 的数据。一个常用的技巧是将 /dev/null 作为重定向的目标文件,以丢弃在某些情况下不需要的输出信息。
例如,可以像这样运行程序并丢弃所有标准输出(而非打印到终端或写入文件):
% verbose_command > /dev/null
从 /dev/null 中读取会始终得到文件结束标记(EOF)。例如,如果打开一个 /dev/null 的文件描述符并尝试调用 read 读取内容,read 将无法读取任何信息并直接返回 0。如果你从 /dev/null 复制到任何文件,目标文件将为 0 字节长度:
% cp /dev/null empty-file
% ls -l empty-file
-rw-rw---- 1 samuel samuel 0 Mar 8 00:27 empty-file
/dev/zero
/dev/zero 设备表现为一个具有无限长度 0 字节的文件。Linux 会为读取进程生成足够的“0”字节返回。
为展示这点,可以运行读数据一节中展示的程序 B.4。这个程序将以十六进制格式输出文件内容。
% ./hexdump /dev/zero
0x000000 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x000010 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x000020 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x000030 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
...
当您确信它将会无休止的继续运行下去请按下 Ctrl+C。
将 /dev/zero 通过 mmap 映射到进程地址空间是一种分配内存的高级技巧。参考进程间通信一章中mmap 的其它用途以及mprotect一节中有关“获得页面对齐的内存”一段内容为例。
/dev/full
特殊文件 /dev/full 表现为位于一个没有剩余空间的文件系统上的文件。向 /dev/full 写入文件将会失败并得到 errno 错误号为 ENOSPC。这个错误一般代表着写入的设备已满。
例如,你可以尝试利用 cp 命令这样写入 /dev/full:
% cp /etc/fstab /dev/full
cp: /dev/full: No space left on device
/dev/full 文件主要用于测试程序在写入文件时遇到空间不足情况的行为。
随机数设备
特殊设备文件 /dev/random 和 /dev/urandom 提供了对 Linux 内核内置的随机数生成器的访问。
多数用于生成随机数的软件函数,例如 C 标准库中的 rand 函数,实际上生成的是 伪随机数(pseudorandom numbers)。尽管这些数字符合随机数的某些特征,它们是可被重现的:如果以相同的种子值开始,你将始终得到同一个序列的伪随机数。这个行为是必然的,因为计算机的本质上是确定及可预知的(deterministic and predictable)。然而,对于某些特定程序而言,这种可预知性是不被期望的;例如,有时候,如果取得加密使用的随机数序列,破解某些加密算法将成为可能。
程序要获得更好的随机数将依赖于来自于系统外的随机性。Linux 内核捕捉了一个非常好的随机性来源:你!通过测量用户输入事件(如击键或鼠标移动等)之间的时间差,Linux 可以生成一个非常好质量的、无法被预知的随机数序列。你可以通过访问 /dev/random 或 /dev/urandom 读取这个数列。读取得到的数据将是随机生成的字节。
这两个设备的区别将在 Linux 用尽已存储的随机数序列时体现出来。如果从 /dev/random 中读取一个很长序列的随机数而不产生任何输入事件(不碰键盘、不移动鼠标或做任何类似的事情),Linux 将会阻塞读取操作。只有当你提供了某些随机性给 Linux 时,系统才会生成更多随机数并返回给程序。