最近在写一个跨平台的文件操作类,其中有一项是获得某目录所在磁盘的剩余空间大小。
在linux上我采用了如下实现:
1. 使用stat(path, &st1)获得该目录的属性,其中的st1.st_dev为该目录所在设备(磁盘)的设备号
2. 使用fp = setmntent(_PATH_MNTTAB) -> mnt = getmntent(fp) 枚举所有挂接点
3. 使用stat(mnt->mnt_fname, &st2)获得挂接设备的属性,其中的st2.st_rdev为该设备的设备号
4. 比较st1.st_dev和st2.st_rdev,如果相等则认为该目录属于设备mnt->mnt_fname
5. 使用statvfs(mnt->mnt_fname, &stfs)获得该设备的文件系统属性,剩余空间大小等于stfs.f_bsize * stfs.f_bavail
起初没有发现什么问题,但后来发现对于一些特殊目录,比如/dev、/proc等,stat函数取到的设备号是无效的数字,对于stat命令也是得到同样的结果。
如:
~$ stat /dev
File: "/dev"
Size: 4040 Blocks: 0 IO Block: 4096 目录
Device: 5h/5d Inode: 4 Links: 17
Access: (0755/drwxr-xr-x) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2012-09-28 00:22:24.627012017 +0800
Modify: 2012-09-28 00:18:54.207012002 +0800
Change: 2012-09-28 00:18:54.207012002 +0800
可以看到设备号居然是0:5(主设备号为0!),这显然很奇怪。
如是我找啊找,终于在man proc里面找到了:
/proc/[pid]/mountinfo (since Linux 2.6.26)
This file contains information about mount points. It contains lines of the form:
36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue
(1)(2)(3) (4) (5) (6) (7) (8) (9) (10) (11)
The numbers in parentheses are labels for the descriptions below:
(1) mount ID: unique identifier of the mount (may be reused after umount(2)).
(2) parent ID: ID of parent mount (or of self for the top of the mount tree).
(3) major:minor: value of st_dev for files on file system (see stat(2)).
于是我:
~$ cat /proc/self/mountinfo
14 19 0:14 / /sys rw,nosuid,nodev,noexec,relatime - sysfs none rw
15 19 0:3 / /proc rw,nosuid,nodev,noexec,relatime - proc none rw
16 19 0:5 / /dev rw,relatime - devtmpfs none rw,size=248400k,nr_inodes=62100,mode=755
17 16 0:11 / /dev/pts rw,nosuid,noexec,relatime - devpts none rw,gid=5,mode=620,ptmxmode=000
18 14 0:15 / /sys/fs/fuse/connections rw,relatime - fusectl fusectl rw
原因找到了。
不过最后我放弃了上述方案去获取磁盘剩余空间,而是直接使用statvfs(path, &stfs) -> stfs.f_bsize * stfs.f_bavail就可以了……
看来我绕了一个大圈,不过却也发现了stat.st_dev的一个潜在的陷阱。