本文共 2659 字,大约阅读时间需要 8 分钟。
设想一个这样的场景: 1. 查看各分区使用情况"df -h",结果如下:(省略不重要的打印) [root test]# df -h Filesystem Size Used Avail Use% Mounted on /dev/mapper/VolGroup-lv_root 49G 19G 28G 40% / 已用19GB,剩余28GB可用,这只是举个例子,每个硬盘的结果不同 2. 在当前目录创建一个1GB大小的文件"fallocate -l 1G gentoo_root.img" 3. 查看各分区使用情况"df -h",结果如下:(省略不重要的打印) [root test]# df -h Filesystem Size Used Avail Use% Mounted on /dev/mapper/VolGroup-lv_root 49G 20G 27G 42% / 已用20GB,剩余27GB可用,很明显,刚才用掉了1GB 4. 一个特殊的程序,源代码如下: #include <stdio.h> #include <unistd.h> #include <sys/stat.h> #include <fcntl.h> #include <error.h> int main(int argc, char *argv[]) { int fd; if ( -1 == (fd = open("./gentoo_root.img", O_RDWR))) { perror("error in open"); return fd; } else { printf("fd is [%d]\n", fd); } sleep(1000); // 让程序程序打开某文件,获得文件描述符,并且维持较长的时间 return 0; // 不会到这一行,实际测试时 } 5. 编译命令如下: gcc ./a.c -Wall -g 6. 运行./a.out [root test]# ./a.out fd is [3] 7. 另起一个终端,用root用户删除之前创建的1GB大小的文件,"rm ./gentoo_root.img" 8. 查看各分区使用情况"df -h",结果如下:(省略不重要的打印) [root test]# df -h Filesystem Size Used Avail Use% Mounted on /dev/mapper/VolGroup-lv_root 49G 20G 27G 42% / 此结果就奇怪了,为什么还是使用了20GB,应该是19GB啊?为什么呢? 9. 使用lsof命令,为了只看重要信息,我们进行一些过滤,"lsof |grep deleted",结果如下: [root test]# lsof |grep deleted vmware-vm 1405 root 3u REG 253,0 2946 1442643 /tmp/vmware-root/apploader-1358.log (deleted) vmtoolsd 1466 root 3u REG 253,0 4138 1442655 /tmp/vmware-root/apploader-1466.log (deleted) smbd 1932 root 2w REG 253,0 51233 1310820 /var/log/samba/log.smbd.old (deleted) smbd 1932 root 8w REG 253,0 51233 1310820 /var/log/samba/log.smbd.old (deleted) a.out 19162 root 3u REG 253,0 1073741824 1835148 /test/gentoo_root.img (deleted) 看最后一行,a.out把持了文件描述符3,a.out的进程号是19162 10. 结束a.out进程,"kill 19162" 11. 查看各分区使用情况"df -h",结果如下:(省略不重要的打印) [root test]# df -h Filesystem Size Used Avail Use% Mounted on /dev/mapper/VolGroup-lv_root 49G 19G 28G 42% / 此时空间回来了!! 上面的所有操作总结一下: 先打开一个文件并保持,然后在操作系统里用root权限用户删除打开的那个文件,然后df统计得到的空间并没有增大,使用lsof查看,发现a.out程序占有了那个文件描述符,然后"杀死"a.out进程,再使用df,发现空间回来了。 我觉得可能原因是这样的: 当一个程序调用open打开某个文件时,操作系统分配一个文件描述符给它(只是一个整数),但是在内核中,有张表,每个文件描述符会对应一个结构体,该文件描述符对应了一个结构体,不然你想啊,一个整数怎么可能记录这么多东西。比如你打开方式是只读还是只写,打开文件的路径等等一些信息(多说一点,socket描述符也是有张表,对应了一个socket的结构体)。然而,当你用root用户去删除那个文件的时候,a.out程序并没有去调用close();,那内核里面那张表中那个文件描述符还是没有回收,唯一内核能知道的就是,对应的文件已经删除了,所以lsof得到的结果后面有(deleted)的标记。那如果a.out去调用read(fd, ...);操作,那么我想它应该会得到错误(因为文件被删了)。而巧的是df的统计方式依然会把没有释放文件描述符的对应的文件也统计进来,所以在删除文件之后,df的结果还是已使用20GB,不是19GB。你可能会觉得它的统计策略不准确,明明我删除了,你为什么还统计?而事实上,如果磁盘满了,但是有个大文件正在被打开着(你可以直接用vim打开着),然后另起终端,用root把该文件删了,此时你再创建一个大文件,你觉得结果是什么?不会是你想要的结果,此时系统会提示你空间不足,直到你结束vim程序,释放那个文件描述符,这部分空间才真的被操作系统认为给释放掉了。 其实还有很多点,由于我了解不深,我也不敢妄下论断,大体上只能这样讲一下我的理解,希望大家补充,指出错误,谢谢。 转载于:https://my.oschina.net/michaelyuanyuan/blog/136496