linux命令du和df的比较(转)

linux du和df的结果不同 why?


        有时我们会看到df和du的结果有比较大的差异,这个现象可能由以下两个原因造成:

        对正在被某进程打开的文件执行了删除操作,在这个进程关闭文件或者退出之前,df的结果还会包括这个被删除的文件,而du不会包括,如果被删除的文件很大,那么这个差异就很明显。

        由于分区A上的某个目录dir上挂载了其他文件系统,而隐藏了原来分区A中dir这棵子树中的文件,造成的结果是,df会统计分区A上的磁盘使用情况,包括被隐藏的部分,而du不会包括被隐藏的部分,却会包括这个目录上挂载的文件系统的空间(du命令的-x选项可以让它只统计一个文件系统内各文件的磁盘使用情况)。这样就会造成不一致。

       应该说这两种情况都不属于异常,也不会对系统造成什么影响。但要想理解造成这个现象的根本原因,就要从du和df的原理说起,最好的办法是借助strace,下面分别是du和df的strace结果:

lstat64("bbb.stp", {st_mode=S_IFREG|0644, st_size=91, ...}) = 0
lstat64("unlink.stp", {st_mode=S_IFREG|0644, st_size=537, ...}) = 0
lstat64("arp.stp", {st_mode=S_IFREG|0644, st_size=236, ...}) = 0
statfs64("/home", 84, {f_type="EXT2_SUPER_MAGIC", f_bsize=4096, f_blocks=12428045,f_bfree=5414268, f_bavail=4772773, f_files=12832512, f_ffree=12739628, f_fsid={0, 0},f_namelen=255, f_frsize=4096}) = 0
write(1, "/dev/sda3 49712180 "..., 62/dev/sda3 49712180 28055108 1909109260% /home) = 62
statfs64("/xen", 84, {f_type="EXT2_SUPER_MAGIC", f_bsize=4096, f_blocks=4923709,f_bfree=1335894, f_bavail=1085783, f_files=2501856, f_ffree=2501838, f_fsid={0,0},f_namelen=255, f_frsize=4096}) = 0
write(1, "/dev/sda5 19694836 "..., 61/dev/sda5 19694836 14351260 434313277% /xen) = 61

        从上面的结果可以看出du是待统计文件(du针对的目录中可见的文件)运行stat这个系统调用,然后计算出磁盘总体的使用情况。因为它的数据是基于文件获取的,所以它有更大的灵活性,不一定要针对整个分区,可以把任何一个目录作为它统计的对象,但是运行的时间可能会比较长。相比之下,df是针对整个文件系统的,它通过系统调用statfs从文件系统的超级块中获取整个文件系统的磁盘使用情况,它没有没法针对任意目录来统计,但速度更快。

         基于以上认识,我们已经可以理解上面提到的第二种情况:分区A的dir子树中的被掩藏的文件du不会统计,因为不是当前可见的,而df还会统计,因为它是针对整个文件系统的,被掩藏的文件终究还是在分区A上没有任何变化,所以df不会受到影响。

        对于上面提到的第一种情况,还需要进一步分析。首先要弄清楚rm操作做了哪些事情,它会真正把文件从磁盘上删除吗?同样地,通过strace命令,我们可以发现rm是通过unlink这个系统调用实现的,这个系统调用的路径如下:
sys_unlink
->do_unlinkat
    ->vfs_unlink
       ->ext3_unlink

        下面是ext3_unlink的主要代码段,可以看出它主要是删除了这个文件对应的目录项,并且把它对应的inode的硬链接个数(i_nlink)减1,然后把这个inode设为dirty,以便稍后同步到磁盘上,并没有完成真正删除文件的失操作。另外需要注意的一点就是,如果这个文件的i_nlink数已经变为0,说明这个文件确实应该从文件,这时ext3_unlink,会把这个inode加到孤儿inode的链表上,所谓孤儿inode就是文件对应的目录项已经不在,但是inode还存在。超级块中的s_last_orphan域会记录最后一个inode号,通过这个inode又可以找到其他的孤儿inode。

retval = ext3_delete_entry(handle, dir, de, bh);
if (retval)
        goto end_unlink;
dir->i_ctime = dir->i_mtime = CURRENT_TIME_SEC;
ext3_update_dx_flag(dir);
ext3_mark_inode_dirty(handle, dir);
inode->i_nlink--;
if (!inode->i_nlink)
   ext3_orphan_add(handle, inode);
inode->i_ctime = dir->i_ctime;
ext3_mark_inode_dirty(handle, inode);
         那么这个inode是什么时候从磁盘上删除的呢?在ext3_unlink执行完返回到do_unlinkat以后,它会调用iput减少内存中的这个文件在vfs这一层对应的inode的引用计数(在进入vfs_unlink之前,先把引用计数i_count加1),如果此时没有其他进程打开这个文件,那么这个inode的引用计数应该变为0,这样inode对象就要从内存中删除,这时iput会调用iput_final-->generic_drop_inode,在generic_drop_inode中会检查这个inode的硬链接数是否为0,也就是上面提到的i_nlink,如果为0会进一步从磁盘上删除这个inode,并把它占用的磁盘块置为可用,当然也会把它从孤儿inode链表中删除,因为它已经被删除了。如果i_nlink不是0,那么就简单地把这个inode对象从内存中释放。
        总结一下,也就是在一个文件对应的inode的引用计数变为0时才会删除,而只有在除删内存中的inode时才会判断它的i_nlink数,即进一步考虑它是否应该从磁盘删除。现在回到上面所说的第一种情况就很容易理解了,删除一个被其他进程打开的文件以后,这个文件的目录项被删除了,但是在do_unlinkat中调用iput时,它的inode引用计数不为0,这个inode就不会从内存中删除,当然更不谈不上从磁盘上删除了,所以这时df仍然会统计到这个文件占用的空间。而当打开这个文件的的进程关闭这个文件,或者进程退出时,在close调用中都会再调用iput,这时引用计数变为0,而i_nlink也早已变成0,那么这个文件就会从磁盘上删除。generic_drop_inode

        那么如果打开文件的进程还没有来得及关闭这个文件,系统突然断电,那么情况又将如何呢? 这个时候肯定会产生孤儿inode(目录项已经被rm删除,而inode还没有来得及从磁盘上删除)但是这一点系统是尽在掌握的,因为在ext3_unlink时这个inode已经被记为孤儿inode了。在重新挂载这个分区时,会调用ext3_fill_super这个函数读取超级块的信息,其中就包括调用ext3_orphan_cleanup清理孤儿inode,这一点可以在日志中得到印证。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值