du和ls命令查看到的文件占用大小不同的问题

工作中遇到在不同的口子中查看的文件夹大小不一致的情况。相差达到50MB,实际确认代码流程是因为一个只对stat得到的st_size进行加总,而另一个对st_size做了4K对齐。而该文件夹中存在了大量只有10几KB的小文件,故而最终导致出现数据不匹配的问题。
在定位过程中发现 du命令与ls 命令对文件大小的展示存在差异,感到好奇,了解了一下原因。
比如下面是一个文件夹,其中只有两个字节为2的文件1和2。ls -alh看到的每个文件的大小只有2个字节,但是du却认为这个文件夹占用了12KB的空间。

 du -h -d 1 ./
12K     ./

ls -alh
total 16K
drwxr-xr-x 2 pi pi 4.0K Sep 25 16:09 .
drwxr-xr-x 3 pi pi 4.0K Sep 25 16:08 ..
-rw-r--r-- 1 pi pi    2 Sep 25 16:08 1
-rw-r--r-- 1 pi pi    2 Sep 25 16:09 2

该问题的原因也是因为文件其实占用磁盘空间的最小单位是块。

du和ls对文件大小打印的源码分析

查看du和ls的源码,分别得到如下对应两段。这里查看的是coreutils-8.26的代码
ls的size打印内容是以如下形式获得的在打印时打印每个文件的st_size,以human_readable来转义成可读的形式

static void
print_long_format (const struct fileinfo *f)
{
...
      char const *size =
        (! f->stat_ok
         ? "?"
         : human_readable (unsigned_file_size (f->stat.st_size),
                           hbuf, file_human_output_opts, 1,
                           file_output_block_size));

      int pad;
      for (pad = file_size_width - mbswidth (size, 0); 0 < pad; pad--)
        *p++ = ' ';
      while ((*p++ = *size++))
        continue;
      p[-1] = ' ';
...
}

而du的整个文件夹大小是如下的形式获得的

static bool
process_file (FTS *fts, FTSENT *ent)
{
...
  const struct stat *sb = ent->fts_statp;
...
  duinfo_set (&dui,
              (apparent_size
               ? MAX (0, sb->st_size)
               : (uintmax_t) ST_NBLOCKS (*sb) * ST_NBLOCKSIZE),
              (time_type == time_mtime ? get_stat_mtime (sb)
               : time_type == time_atime ? get_stat_atime (sb)
               : get_stat_ctime (sb)));
...
  duinfo_add (&tot_dui, &dui);
...

stat-size.h
...
# if defined _POSIX_SOURCE || !defined BSIZE
#  define ST_NBLOCKS(statbuf) \
  ((statbuf).st_size / ST_NBLOCKSIZE + ((statbuf).st_size % ST_NBLOCKSIZE != 0))
# else
...
#ifndef ST_NBLOCKSIZE
# ifdef S_BLKSIZE
#  define ST_NBLOCKSIZE S_BLKSIZE
# else
#  define ST_NBLOCKSIZE 512
# endif
#endif
...

其中,sb来源于FTSENT *ent ,coreutils中使用了FTS来做遍历,这块没有具体深究。计入大小时判断了apparent_size的flag,该选项通过–apparent-size置位,如果该标记位被设置,就会打印原始的大小,否则就会按照 ST_NBLOCKS 计算对齐结果,再乘以块大小。我理解这里的 ST_NBLOCKSIZE 会被定义为S_BLKSIZE,在一般情况下应该是 4096字节。

获取文件块大小的方法

参考自https://www.cnblogs.com/kerrycode/p/12764361.html

通过stat命令查看文件大小的 IO Block 即为文件系统的块大小。

$stat 1
  File: ‘1’
  Size: 2               Blocks: 8          IO Block: 4096   regular file
Device: b302h/45826d    Inode: 655538      Links: 1
Access: (0644/-rw-r--r--)  Uid: ( 1000/      pi)   Gid: ( 1000/      pi)
Access: 2022-09-25 16:08:26.079696886 +0800
Modify: 2022-09-25 16:08:26.079696886 +0800
Change: 2022-09-25 16:08:26.099696755 +0800
 Birth: -
#直接查看文件系统的整体情况,Block size 即表征块大小
$ stat -f /
  File: "/"
    ID: ad90cd3bc2f307cb Namelen: 255     Type: ext2/ext3
Block size: 4096       Fundamental block size: 4096
Blocks: Total: 3731136    Free: 1505734    Available: 1339574
Inodes: Total: 947232     Free: 743546

相关知识点

在确认该问题根因过程中也发现了其他一些有意思的点,在这里一并记录下。
1、du命令流程图
coreutil的源码在如下地址下载:https://www.gnu.org/software/coreutils/
coreutil的核心流程可以在如下的网址查看,:
http://www.maizure.org/projects/decoded-gnu-coreutils/du.html
这里附一下du的执行流程图:
在这里插入图片描述

2、windows文件系统中,几十字节的文件空间占用只有0的现象。
该点是因为NTFS的文件信息都放在MFT里,每个MFT的记录是1KB大小,每个记录对应一个文件。
其中,有个叫$DATA的属性,当文件小的时候,$DATA里放的就是文件内容,当文件大的时候$DATA放的是指针,指向另外一个区域保存更大的数据。
参考自知乎:https://www.zhihu.com/question/23972911

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值