Linux下应先用statfs定位挂载点,再open对应块设备并ioctl(BLKSSZGET/BLKPBSZGET)分别获取逻辑与物理扇区大小;Windows需用DeviceIoControl+IOCTL_DISK_GET_DRIVE_GEOMETRY_EX查询BytesPerSector和PhysicalBytesPerSector。

Linux 下用 statfs 和 ioctl 获取挂载点与逻辑/物理扇区大小
直接读 /proc/mounts 只能拿到挂载路径和文件系统类型,拿不到扇区大小;statvfs 也不提供物理扇区信息。真实需求(比如做底层块设备对齐、RAID 配置校验)必须区分逻辑扇区(LOGICAL_BLOCK_SIZE)和物理扇区(PHYSICAL_BLOCK_SIZE),这两者在 SSD 或 Advanced Format 硬盘上常不一致。
核心做法是:先通过 statfs 或遍历 /proc/mounts 定位挂载点 → 对挂载点路径调用 open("/dev/xxx", O_RDONLY) 拿到块设备 fd → 再用 ioctl(fd, BLKSSZGET, &logical) 和 ioctl(fd, BLKPBSZGET, &physical) 分别获取。
-
BLKSSZGET返回逻辑扇区大小(通常是 512 或 4096),BLKPBSZGET是内核 2.6.32+ 才支持的物理扇区查询,老内核会返回EINVAL - 注意:不能对普通文件或挂载点目录直接
ioctl,必须打开对应底层块设备。可从/sys/block/*/slaves/或readlink /sys/block/*/device反查,但更稳的方式是解析/proc/self/mountinfo中的major:minor,再映射到/dev下设备 - 示例片段:
int fd = open("/dev/sda1", O_RDONLY);<br>int logical = 0, physical = 0;<br>ioctl(fd, BLKSSZGET, &logical);<br>ioctl(fd, BLKPBSZGET, &physical); // 可能失败,需检查 errno
Windows 下用 GetDiskFreeSpaceEx 和 DeviceIoControl 获取卷信息
Windows 没有“挂载点”概念,只有卷(volume)和驱动器号(如 C:)。GetDiskFreeSpaceEx 能拿到簇大小(即文件系统分配单元),但它不是扇区大小——NTFS 默认簇是 4KB,而底层磁盘扇区可能是 512B 或 4KB。
真正需要扇区大小时,必须走设备控制接口:CreateFile(L"\.C:", ...) 打开卷设备 → DeviceIoControl(..., IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, ...) → 结构体中 DiskGeometry.BytesPerSector 是逻辑扇区,DiskGeometry.PhysicalBytesPerSector(Win8+)才是物理扇区。
立即学习“C++免费学习笔记(深入)”;
- 打开卷设备时权限要足:
GENERIC_READ+FILE_SHARE_READ | FILE_SHARE_WRITE,否则CreateFile失败 -
IOCTL_DISK_GET_DRIVE_GEOMETRY_EX在 Windows 7 及以下不返回PhysicalBytesPerSector字段,需降级用IOCTL_STORAGE_QUERY_PROPERTY查StorageAccessAlignmentProperty - 不要用
GetDriveType判断是否为硬盘——RAM disk、iSCSI 卷也返回DRIVE_FIXED,得结合IOCTL_STORAGE_GET_DEVICE_NUMBER看设备类型
跨平台封装时绕不开的设备路径映射问题
Linux 的 /dev/sda1 和 Windows 的 \\.\C: 表面都是“设备路径”,但语义完全不同:前者是块设备节点,后者是卷设备句柄。同一块 SSD 在 Linux 下可能有多个分区(sda1, sda2),在 Windows 下却只对应一个卷(C:),且没有标准 API 把 C: 映射回 sda1 ——因为 Windows 不暴露底层分区名。
这意味着:跨平台工具若想统一处理,必须放弃“用一个字符串表示设备”的幻想,改用两级抽象:
- 第一层:运行时识别当前 OS + 获取所有活跃挂载/卷列表(Linux 读
/proc/mounts,Windows 调GetVolumePathNamesForVolumeNameW) - 第二层:对每个条目,按平台规则构造设备访问路径(Linux 查
/sys/dev/block/M:m,Windows 用FindFirstVolume+GetVolumeNameForVolumeMountPoint) - 常见坑:
statfs对/boot返回的f_fsid和stat对/dev/sda1的st_dev无直接关系;Windows 的QueryDosDevice也不能把C:变成DeviceHarddiskVolume1的稳定映射
实际应用中扇区大小误判导致的典型故障
很多工具(如自定义日志写入器、数据库页刷盘模块)假设扇区是 512B,直接按 512 对齐 buffer,结果在 4K 扇区硬盘上触发“读-修改-写”放大,性能暴跌 3–5 倍。这不是理论风险,而是线上真实发生过的 case。
- 最隐蔽的坑:
posix_memalign分配 4096 对齐内存 ≠ 确保 I/O 对齐——你还得用O_DIRECT(Linux)或FILE_FLAG_NO_BUFFERING(Windows),且 buffer 地址、offset、length 全部按物理扇区对齐 -
hdparm -I /dev/sda输出里的 “Logical/Physical Sector size” 是权威来源,但 C++ 程序不能依赖外部命令——必须走系统调用,否则容器环境或最小化系统里hdparm根本不存在 - 虚拟化环境(如 KVM/QEMU)下,guest OS 看到的扇区大小由 qemu 参数(
logical_block_size=4096,physical_block_size=4096)决定,和宿主机硬盘无关,这点常被忽略


















