Linux 应优先读取 /sys/block/*/queue/physical_block_size 获取物理扇区大小,Windows 应通过 WMI 查询 Win32_DiskDrive.BytesPerSector;二者均比 statfs/GetDiskFreeSpaceEx 更准确,且无需特权。

Linux 下用 statfs 和 ioctl 获取挂载点与逻辑块大小
直接读取挂载点信息不能只靠 getmntent,它不提供扇区大小;而 statfs 可以拿到文件系统层面的 f_bsize(推荐 I/O 块大小)和 f_frsize(基本分配单元),但注意:这不是物理扇区大小,而是文件系统对齐单位。要拿物理扇区大小,必须用 ioctl 调用 BLKSSZGET。
常见错误是把 f_bsize 当成磁盘物理扇区——比如 ext4 默认返回 4096,但它可能运行在 512B 或 4K 物理扇区的硬盘上。
- 先用
statfs("/path", &buf)拿到挂载点的f_bsize和f_frsize - 再用
open("/dev/sda1", O_RDONLY|O_CLOEXEC)打开对应设备(需从/proc/mounts或libblkid反查设备路径) - 调用
ioctl(fd, BLKSSZGET, &phys)得到物理扇区大小(单位字节) - 别忘了
close(fd),否则多次调用会泄漏 fd
Windows 下用 GetDiskFreeSpaceEx 和 DeviceIoControl 获取卷信息
Windows 没有统一“挂载点”概念,但每个驱动器号(如 C:)本质是卷的符号链接。用 GetDiskFreeSpaceEx 只能拿到簇大小(lpSectorsPerCluster * lpBytesPerSector),而 lpBytesPerSector 是操作系统报告的“扇区大小”,未必等于物理值。
真正物理扇区需走 DeviceIoControl + IOCTL_DISK_GET_DRIVE_GEOMETRY_EX,它返回 DISK_GEOMETRY_EX 结构体里的 Geometry.BytesPerSector 字段。
-
GetDiskFreeSpaceEx的lpBytesPerSector实际是逻辑扇区大小,常为 512 或 4096,受存储堆栈影响(如 BitLocker、虚拟磁盘) - 必须用
CreateFile("\\.\C:", ...)打开卷设备句柄,而非"C:\" -
DeviceIoControl需要管理员权限才能获取某些物理属性,非管理员下可能返回 512 即使硬盘是 4K - 注意
DISK_GEOMETRY_EX中的Geometry.BytesPerSector是物理扇区,Geometry.ExtraBytes是逻辑到物理的偏移(用于对齐计算)
跨平台统一获取:优先解析 /sys/block/(Linux)和 WMI(Windows)
硬编码 ioctl 或 DeviceIoControl 容易出错,尤其在容器、LVM、NVMe 多命名空间场景下。更健壮的做法是绕过 C++ 系统调用,直接读取内核暴露的 sysfs 或调用系统管理接口。
Linux 下,/sys/block/sda/sda1/size 给扇区总数,/sys/block/sda/queue/logical_block_size 和 /sys/block/sda/queue/physical_block_size 分别给出逻辑与物理扇区大小,无需 root 权限,且结果准确。
Windows 下,WMI 查询 Win32_Volume 可得 DriveLetter 和 Capacity,再用 Win32_DiskDrive 关联物理磁盘,其 BytesPerSector 字段即物理扇区大小。
- Linux 上解析
/sys/block/*/queue/是最轻量、最可靠方式,比 ioctl 更少权限依赖 - Windows 上 WMI 不需要管理员权限即可读取
BytesPerSector,但要注意 WMI 服务是否启用 - 避免用
lsblk -o NAME,MOUNTPOINT,LOG-SEC,PHY-SEC这类命令行工具做自动化——输出格式不稳定,不同发行版字段顺序可能变
实际应用中容易忽略的对齐陷阱
拿到物理扇区大小只是第一步。真实 I/O 性能瓶颈往往来自未对齐访问:比如在 4K 物理扇区硬盘上,用 512B 对齐的 buffer 去 read(),会触发 read-modify-write(RMW)放大延迟。
Linux 的 O_DIRECT 要求 buffer 地址和 I/O size 都按物理扇区对齐;Windows 的 FILE_FLAG_NO_BUFFERING 同样要求起始地址和 length 是扇区大小的整数倍。
- 不要只检查
posix_memalign是否成功,还要验证(uintptr_t)ptr % phys_sector== 0 - 使用
ioctl(fd, BLKPBSZGET, &phys)(比BLKSSZGET更准,专指物理块大小)代替旧接口 - NVMe 设备可能报告多个命名空间(namespace)各自有不同的扇区大小,
/sys/class/nvme/nvme0/nvme0n1/下才有对应 queue 属性 - 虚拟机里看到的“物理扇区”其实是 hypervisor 模拟的,真实值需查宿主机,这点常被忽略
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











