第 11 章. 裸设备

裸设备可以绑定到现有的块设备(例如磁盘),并用于对该现有块设备执行“原始”I/O。这种“原始”I/O 绕过了通常与块设备关联的缓存。因此,裸设备提供了一条通往物理设备的更“直接”的路径,并允许应用程序对物理设备的 I/O 时序进行更多控制。这使得裸设备适用于复杂的应用程序,例如数据库管理系统,这些系统通常进行自己的缓存。

裸设备是字符设备(主设备号 162)。第一个次设备号(即 0)被保留作为控制接口,通常位于/dev/rawctl。一个名为 raw 的实用程序(参见 man raw)可以用来将裸设备绑定到现有的块设备。“现有块设备”可以是磁盘或 cdrom/dvd,它们的底层接口可以是 Linux 支持的任何类型(例如 IDE/ATA 或 SCSI)。

在我的系统上,列出裸设备,然后绑定 SCSI 磁盘分区,再绑定整个磁盘的命令序列如下所示

$ ls -lR /dev/raw*
crw-r--r--    1 root     root     162,   0 Dec  6 06:54 /dev/rawctl

/dev/raw:
total 0
crw-r--r--    1 root     root     162,   1 Dec  6 06:54 raw1
crw-r--r--    1 root     root     162,   2 Dec  6 06:54 raw2
crw-r--r--    1 root     root     162,   3 Dec  6 06:54 raw3
crw-r--r--    1 root     root     162,   4 Dec  6 06:54 raw4
$
$ raw -qa
$
$ raw /dev/raw/raw1 /dev/sda3
/dev/raw/raw1:  bound to major 8, minor 3
$ raw /dev/raw/raw2 /dev/sda
/dev/raw/raw2:  bound to major 8, minor 0
$ raw -qa
/dev/raw/raw1:  bound to major 8, minor 3
/dev/raw/raw2:  bound to major 8, minor 0

用于字符设备的常规系统调用数组在裸设备上可用。read(2) 和 write(2) 的传输大小必须是物理设备块大小的整数倍。对于磁盘,这将是其扇区大小,通常为 512 字节。提供给 read() 和 write() 系统调用的数据缓冲区必须与块大小对齐。lseek(2) 调用也需要将其文件读/写偏移量与块边界对齐。pread(3) 调用(参见 man pread)结合了 read() 和 lseek(),并且在裸设备上可能很有用(pwrite() 也是如此)。在 32 位架构上,当 “off_t” 类型为 32 位长时,应注意大于 2 GB(或可能 4 GB)的偏移量。一种解决方案是使用 _llseek() 调用(参见 man llseek)。

诸如最新版本的 ddlmdd (来自 lmbench 程序套件) 等 Unix 实用程序可以用于在“裸”设备之间移动数据,因为它们满足上述块对齐要求。sg_utils 包中的最新版本的 sg_dd 命令可以访问裸设备和 sg 设备。

另请注意,如果物理设备具有奇数个扇区(如 blockdev --getsize /dev/raw/raw* 显示的那样),则最后一个扇区将无法使用原始 I/O 访问。

Warning

如果通过绑定的裸设备和通过其正常的块接口访问块设备,则两种访问机制之间没有缓存一致性。例如,如果/dev/sda1既已挂载又通过绑定的裸设备访问,则可能存在数据不一致。