工作中遇到在不同的口子中查看的文件夹大小不一致的情况。相差达到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