裸设备可以绑定到现有的块设备(例如磁盘),并用于对该现有块设备执行“原始”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)。
诸如最新版本的 dd 和 lmdd (来自 lmbench 程序套件) 等 Unix 实用程序可以用于在“裸”设备之间移动数据,因为它们满足上述块对齐要求。sg_utils 包中的最新版本的 sg_dd 命令可以访问裸设备和 sg 设备。
另请注意,如果物理设备具有奇数个扇区(如 blockdev --getsize /dev/raw/raw* 显示的那样),则最后一个扇区将无法使用原始 I/O 访问。
![]() | 如果通过绑定的裸设备和通过其正常的块接口访问块设备,则两种访问机制之间没有缓存一致性。例如,如果/dev/sda1既已挂载又通过绑定的裸设备访问,则可能存在数据不一致。 |