先让我们来了解下:
回环设备(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 时,系统才会生成更多随机数并返回给程序。
例如,尝试用
od 命令我们在这里使用 od 代替行为相当接近的、B.4 中的 hexdump 命令,因为 hexdump 会在没有数据时直接退出,而 od 会等待更多的输入。指定的 -t x1 擦书会提示 od 以十六进制格式输出文件内容。输出 /dev/random 的内容。每行输出将显示 16 个随机的字节。
% od -t x1 /dev/random
0000000 2c 9c 7a db 2e 79 3d 65 36 c2 e3 1b 52 75 1e 1a
0000020 d3 6d 1e a7 91 05 2d 4d c3 a6 de 54 29 f4 46 04
0000040 b3 b0 8d 94 21 57 f3 90 61 dd 26 ac 94 c3 b9 3a
0000060 05 a3 02 cb 22 0a bc c9 45 dd a6 59 40 22 53 d4
你将看到的输出的行数可能不同——可能很少——但在 Linux 耗尽存储的随机性信息时,输出最终会暂停。这个时候,尝试移动鼠标或在键盘上输入,你将观察到 od 会输出一些额外的字节。为得到更好的随机性,让你的猫在键盘上走一遭吧
:)
对比之下,对 /dev/urandom 的读操作将不会被阻塞。如果 Linux 耗尽了随机性数据,它会通过某种加密算法从之前的随机字节序列中生成更多的伪随机数。尽管对于很多情况来说,这样得到的随机数已经符合要求,仍然会有一些测试将允许
/dev/random 生成的序列通过而这个序列失败。
举例说明,如果你执行下面的命令,在 Ctrl+C 被按下之前你将看到无止尽的随机字符从眼前飘过:
% od -t x1 /dev/urandom
0000000 62 71 d6 3e af dd de 62 c0 42 78 bd 29 9c 69 49
0000020 26 3b 95 bc b9 6c 15 16 38 fd 7e 34 f0 ba ce c3
0000040 95 31 e5 2c 8d 8a dd f4 c4 3b 9b 44 2f 20 d1 54
...
在程序中使用 /dev/random 的随机数非常简单。列表 6.1 展示了一个通过从 /dev/random 中读取的字节生成随机数的函数。请记住,/dev/random 会将读操作阻塞直到系统有足够的随机数提供给程序;如果对随机数的质量的要求不那么高而更多要求程序能快速执行,可以用
/dev/urandom 代替。
列表 6.1 (random_number.c)利用 /dev/random 的随机数生成函数
#include <assert.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
/* 返回MIN和MAX之间(包含MIN和MAX)的一个随机整数。从 /dev/random获得 随机性。 */
int random_number (int min, int max)
{
/* 在一个静态变量中存储打开/dev/random的文件描述符。这样我们就不需要在每次调用这个函数时都打开文件。*/
static int dev_random_fd = -1;
char* next_random_byte;
int bytes_to_read;
unsigned random_value;
/* 确保 MAX比MIN大。 */
assert (max > min);
/* 如果这是这个函数第一次被调用,打开/dev/random里的文件描述符。*/
if (dev_random_fd == -1) {
dev_random_fd = open (“/dev/random”, O_RDONLY);
assert (dev_random_fd != -1);
}
/* 读足够的随机数来填充一个整型变量 。*/
next_random_byte = (char*) &random_value;
bytes_to_read = sizeof (random_value);
/* 直到我们已经读取了足够的字节时停止循环。因为/dev/random包含用户动作产生的随机数,
读操作也许会被阻塞并且可能一次只返回一个单个的随机字节。*/
do {
int bytes_read;
bytes_read = read (dev_random_fd, next_random_byte, bytes_to_read);
bytes_to_read
-= bytes_read;
next_random_byte += bytes_read;
} while (bytes_to_read > 0);
/*通过运算,使随机数保持在正确范围内。*/
return min + (random_value % (max - min + 1));
}
回环设备(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。