Linux用ioctl(BLKSSZGET)、Windows用DeviceIoControl(IOCTL_DISK_GET_DRIVE_GEOMETRY_EX)获取物理扇区大小,需管理员权限和块设备句柄;O_DIRECT或FILE_FLAG_NO_BUFFERING要求内存地址、长度、偏移三者均对齐该大小。

Linux 下用 ioctl + BLKSSZGET 获取物理扇区大小
Linux 内核暴露了块设备的物理扇区大小(通常叫 logical / physical block size),但不能靠文件系统路径直接查,得打开底层块设备文件(比如 /dev/sda)再调 ioctl。注意:普通用户没权限读块设备,必须加 sudo 或改udev规则。
-
open("/dev/sda", O_RDONLY)后对 fd 调ioctl(fd, BLKSSZGET, &physical),physical就是物理扇区字节数(常见 512 或 4096) - 别用
statfs或fstat——它们返回的是文件系统逻辑块大小,和磁盘真实对齐无关 - 如果
ioctl返回 -1 且errno == ENOTTY,说明 fd 不指向块设备(比如你误传了普通文件路径) - 别在挂载点(如
/mnt/data)上 open ——它不是块设备,会失败
Windows 下用 DeviceIoControl 查询 IOCTL_DISK_GET_DRIVE_GEOMETRY_EX
Windows 没有统一的“物理块大小”API,最可靠的是获取驱动器几何结构里的 Geometry.BytesPerPhysicalSector 字段。这需要管理员权限,且只能对 \.PhysicalDriveX 这类句柄生效。
- 先用
CreateFile(L"\\.\PhysicalDrive0", GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr) - 再传
IOCTL_DISK_GET_DRIVE_GEOMETRY_EX,结构体里BytesPerPhysicalSector才是你要的值 - 若返回 0 或
GetLastError()是ERROR_ACCESS_DENIED,基本就是权限不足 —— Visual Studio 要以管理员身份运行,命令行也要用管理员 cmd - 别用
GetDiskFreeSpace:它只给簇大小(file system allocation unit),不是物理对齐依据
为什么不能只靠 posix_memalign + read/write 就完事?
即使内存地址对齐、IO 缓冲区长度是物理块整数倍,仍可能因内核页缓存或文件系统层导致非对齐 IO。关键在于绕过缓存并确保底层块对齐。
- Linux 必须加
O_DIRECT标志 open 文件(不只是块设备),否则内核会把你的请求拆成任意大小的内部缓冲区 -
O_DIRECT要求:缓冲区地址对齐到getpagesize()(通常是 4096),缓冲区长度是物理扇区大小整数倍,文件偏移也得是对齐的 - Windows 对应的是
FILE_FLAG_NO_BUFFERING,同样要求内存地址、长度、文件偏移三者都按物理扇区对齐 - 漏掉任一条件,
read/write会直接失败并返回EINVAL(Linux)或ERROR_INVALID_PARAMETER(Windows)
C++ 跨平台对齐读写的实际检查点
写完代码别急着测吞吐,先确认这几件事是否全满足,否则性能不升反降,甚至读出错数据。
- 物理块大小必须从块设备查,不是从文件路径、不是从
stat、不是硬编码 4096 - 内存分配必须用
posix_memalign(Linux/macOS)或_aligned_malloc(Windows),new和malloc不保证对齐 - 文件打开时 Linux 加
O_DIRECT,Windows 加FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH - 每次
read/write的offset、length、缓冲区地址,三者模物理块大小都必须为 0
最容易被忽略的是:同一块硬盘不同分区可能对应不同物理扇区大小(尤其 SMR 盘或虚拟化环境),所以千万别缓存一次查询结果就全局复用。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











