Linux下如何访问SATA块设备报告容量以外的存储空间?
嘿,这个问题我碰到过好几次了——Linux块设备子系统确实会死死盯着设备报告的容量,超出就给你扔EINVAL。不过不用改内核或者驱动,有几个用户空间的办法能绕过去,我给你捋捋:
方法1:使用SCSI Passthrough直接发送底层命令
这是最可靠的方案,直接绕过块设备层的容量检查,向SATA/SCSI设备发送原始读写命令,完全不受系统记录的容量限制。
用工具快速验证
可以用sg3_utils包自带的工具(比如sg_read、sg_write)直接指定超出报告容量的LBA进行操作:
# 读取LBA为100000000的扇区(假设设备报告容量仅到90000000) sg_read --lba=100000000 --count=512 /dev/sg0
这里的/dev/sg0是对应SATA设备的sg节点,你可以用lsscsi命令找到自己设备对应的sg路径。
自己编写代码实现
如果要在程序里集成这个功能,可以通过ioctl(SG_IO)发送SCSI命令。下面是一个简单的读取示例:
#include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/ioctl.h> #include <scsi/sg.h> #define BUF_SIZE 512 int main() { int fd = open("/dev/sg0", O_RDWR); if (fd < 0) { perror("打开sg设备失败"); return 1; } unsigned char buf[BUF_SIZE]; sg_io_hdr_t io_hdr; unsigned char cmd[16]; // 用READ(16)命令支持更大的LBA范围 // 构造READ(16)命令:读取LBA=0x186A0000(十进制100000000)的1个扇区 memset(cmd, 0, sizeof(cmd)); cmd[0] = 0x88; // READ(16)操作码 // 填充LBA的4个字节(大端序) cmd[2] = (0x186A0000 >> 24) & 0xFF; cmd[3] = (0x186A0000 >> 16) & 0xFF; cmd[4] = (0x186A0000 >> 8) & 0xFF; cmd[5] = 0x186A0000 & 0xFF; cmd[7] = 0x00; // 扇区数高字节(1扇区对应0x0001) cmd[8] = 0x01; // 扇区数低字节 memset(&io_hdr, 0, sizeof(io_hdr)); io_hdr.interface_id = 'S'; io_hdr.cmdp = cmd; io_hdr.cmd_len = sizeof(cmd); io_hdr.dxferp = buf; io_hdr.dxfer_len = BUF_SIZE; io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; io_hdr.timeout = 10000; // 10秒超时 if (ioctl(fd, SG_IO, &io_hdr) < 0) { perror("SG_IO命令执行失败"); close(fd); return 1; } // 检查命令执行结果 if ((io_hdr.info & SG_INFO_OK_MASK) != SG_INFO_OK) { fprintf(stderr, "读取命令执行失败\n"); close(fd); return 1; } printf("读取成功\n"); close(fd); return 0; }
编译时记得链接sg库:gcc read_sg.c -o read_sg -lsg
方法2:临时修改块设备的容量记录
如果不想直接处理底层SCSI命令,可以临时修改Linux系统中记录的块设备容量,这样普通的lseek和读写操作就能正常工作了。
用blockdev命令设置新的容量(单位是字节):
# 假设实际可访问空间是1TB(1099511627776字节),替换成你的实际值 blockdev --setsize64 /dev/sda 1099511627776
这个修改是临时的,系统重启后会自动恢复为设备报告的原始容量。修改完成后,你就可以像操作普通块设备一样,用open/lseek/read/write访问超出原报告容量的区域了。
注意:一定要确认设备确实有这么大的可访问空间!如果设置的容量超过设备实际物理空间,读写操作会触发IO错误,甚至可能损坏设备。
方法3:使用raw设备(仅部分系统支持)
有些Linux发行版还保留了raw设备支持,可以将块设备绑定到raw节点,绕过块设备层的部分限制。
步骤如下:
- 加载raw模块:
modprobe raw - 绑定设备:
raw /dev/raw/raw1 /dev/sda - 直接操作
/dev/raw/raw1,此时lseek可能不再受原容量限制
不过这个方法现在已经逐渐被淘汰,很多现代Linux系统不再默认支持raw设备,优先级不如前两种方法。
重要提醒
无论用哪种方法,访问设备报告容量以外的区域都有风险:
- 这些区域可能是厂商预留的固件存储区、坏块替换区,写入数据可能损坏设备固件或导致设备不稳定;
- 如果设备实际物理空间并没有这么大,读写操作会返回IO错误,甚至可能丢失已有数据;
- 操作前务必备份所有重要数据,并先做小范围测试。
内容的提问来源于stack exchange,提问作者trinof

