为啥shutils和df报告的磁盘大小有百分之几的差异?

Posted

技术标签:

【中文标题】为啥shutils和df报告的磁盘大小有百分之几的差异?【英文标题】:Why is there a few % difference in disk size reported by shutils and df?为什么shutils和df报告的磁盘大小有百分之几的差异? 【发布时间】:2019-11-12 05:35:28 【问题描述】:

我正在编写一个简单的监控脚本,我想在其中添加磁盘空间检查。然而,我发现系统dfshutils.disk_usage() 之间报告的可用空间不同。

在安装了三个磁盘的系统上:

# df / /mnt/2TB1 /mnt/1TB1
Filesystem      1K-blocks       Used Available Use% Mounted on
/dev/sda1       472437724  231418380 216997128  52% /
/dev/sdb1      1921802520 1712163440 111947020  94% /mnt/2TB1
/dev/sdc1       960380648  347087300 564438888  39% /mnt/1TB1

# python3
Python 3.6.8 (default, Jan 14 2019, 11:02:34)
[GCC 8.0.1 20180414 (experimental) [trunk revision 259383]] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import shutil
>>> (t, u, f) = shutil.disk_usage('/')
>>> (t, u, f)
(483776229376, 236973805568, 222203674624)
>>> u/t
0.48984177224594366
>>> (t, u, f) = shutil.disk_usage('/mnt/2TB1')
>>> (t, u, f)
(1967925780480, 1753255362560, 114633748480)
>>> u/t
0.8909153891628782
>>> (t, u, f) = shutil.disk_usage('/mnt/1TB1')
>>> (t, u, f)
(983429783552, 355400192000, 578002624512)
>>> u/t
0.361388477290517

相差分别为3%、5%和3%。它来自哪里,哪个结果是正确的?

【问题讨论】:

你能把ut的值贴出来吗?目前我们不知道这些值中的哪一个与df 的值不同。 @Socowi:你说得对——我用信息更新了问题 【参考方案1】:

曾经 1Gb 是 1024 兆字节,但制造商发现了一个营销技巧,将 50000 兆字节称为 50 Gb 后,就搞砸了。

所以区别在于这些软件实现如何处理这些 Megas,无论是 1000 还是 1024。

【讨论】:

我认为这不是问题。 dfshutil报告的数字以字节为单位;没有 k/ki, M/Mi, G/Gi, ... 只是简单的字节。即使dfshutil 之间的单位不同:百分比也不应该改变。 我必须纠正自己。 OP 的 df 输出以 1024 字节为单位列出大小。 shutil 使用字节作为单位。但是,无论如何,百分比应该是相同的。【参考方案2】:

Python 似乎有正确的结果。 默认情况下,[man7]: DF(1) (man df) 以 1 KiB 块显示数字(大小)。但是,考虑到运算(除以 1024)同时应用于除数和除数(计算百分比时),它会自行减少,因此它与最终结果无关。

示例(对于某个目录):

    运行df(默认以KiB输出) 运行df -B 1(以字节为单位输出)

    运行以下 Python 脚本:

    import sys, shutil
    
    path = sys.argv[1] if len(sys.argv) > 1 else "/"
    t, u, f = shutil.disk_usage(path)
    percent = 100 * u / t
    print("(Python) - Volume name\t: : : :.3f% (:.0f) :".format(t, u, f, percent, percent, path))
    
[cfati@cfati-ubtu16x64-0:~]> for f in "/" "/media/sf_shared_00"; do echo df "$f" && df $f && echo df -B 1 "$f" && df -B 1 $f && echo Python script on "$f" && python3 -c "import sys, shutil; path = sys.argv[1] if len(sys.argv) > 1 else \"/\"; t, u, f = shutil.disk_usage(path); percent = 100 * u / t; print(\"(Python) - Volume name\t: : : :.3f% (:.0f) :\".format(t, u, f, percent, percent, path))" $f && echo && echo; done
df /
Filesystem                                   1K-blocks     Used Available Use% Mounted on
/dev/mapper/ubtu16x640_lvg0-ubtu16x640_root0 102067544 10999896  85859792  12% /
df -B 1 /
Filesystem                                      1B-blocks        Used   Available Use% Mounted on
/dev/mapper/ubtu16x640_lvg0-ubtu16x640_root0 104517165056 11263893504 87920427008  12% /
Python script on /
(Python) - Volume name  104517165056 11263893504 87920427008 10.777% (11) /


df /media/sf_shared_00
Filesystem     1K-blocks      Used Available Use% Mounted on
shared_00      327679996 155279796 172400200  48% /media/sf_shared_00
df -B 1 /media/sf_shared_00
Filesystem        1B-blocks         Used    Available Use% Mounted on
shared_00      335544315904 159006511104 176537804800  48% /media/sf_shared_00
Python script on /media/sf_shared_00
(Python) - Volume name  335544315904 159006511104 176537804800 47.388% (47) /media/sf_shared_00

如图所示,步骤 #2. 中的数字(大小)与步骤 #3. 中的数字相同。 。计算百分比(在任何 3 种情况下),Python 百分比似乎是正确的。

我不清楚为什么 df 会报告这些百分比(没有查看源代码),但可能是(出现的一切纯属推测):

它倾向于保护用户(报告的百分比比实际高一点) 它与逻辑磁盘单元(扇区)有关。 例如在 4 KiB (4096) 扇区磁盘上,一个 4097 字节的文件将占用(通常为 4097 字节),但考虑到磁盘逻辑单位是扇区(而不是字节 - 这在某种程度上类似于 #pragma pack),文件将占用 2 个扇区(8 KiB),因此其基础大小将大于报告的一个

【讨论】:

【参考方案3】:

正如ChristiFati 已经指出的那样,两种工具的比率used / total 相同,但df 报告的Use% 字段与100 · used / total 不同。

例如,让我们检查安装在/ 上的/dev/sda1 的值。

df.total = 472437724 df.used = 231418380 df.available = 216997128 df.percentage = 52

shutil.total = 483776229376 shutil.used = 236973805568 shutil.free = 222203674624

df.used / df.total = 0.4898 = shutil.free / shutil.total但是…… df.used / df.total = 0.4898    0.52 = df.percentage / 100

coreutils 的df 实现的source code 为这个问题提供了一些启示。 three lines 1171-1173 是相关的。 pct 是百分比。

uintmax_t u100 = v->used * 100; uintmax_t nonroot_total = v->used + v->available; pct = u100 / nonroot_total + (u100 % nonroot_total != 0);

正如我们所见,df 不计算 used / total,而是计算 used / (used + free)。注意used + free < total

我怀疑……

total 包括为元数据保留的空间,例如文件系统中哪个文件驻留的位置(取决于文件系统,这可能包括胖表、inode 等)。由于您不能将该空间用于常规文件,因此 Use% 中的空间被排除在外,而是使用不包含元数据的 (used + free)

然而,一项测试表明……

这不可能是完整的故事。以下脚本在 2 MiB 文件内生成 FAT12 和 ext2 文件系统。该脚本必须使用sudo 执行。

#! /bin/bash

check() 
  head -c 2MiB /dev/zero > fs
  mkfs."$@" fs
  mkdir fsmount
  mount -o loop fs fsmount
  df fsmount
  umount fsmount
  rm -r fs fsmount


echo fat12:
check fat -F 12

echo ext2:
check ext2

我得到了输出

fat12:
[...]
Filesystem     1K-blocks  Used Available Use% Mounted on
/dev/loop0          2028     0      2028   0% /tmp/fsmount
ext2:
[...]                           
Creating filesystem with 2048 1k blocks and 256 inodes
[...]
Filesystem     1K-blocks  Used Available Use% Mounted on
/dev/loop0          2011    21      1888   2% /tmp/fsmount

请注意,两种情况下的总大小都小于文件系统,即 2048 KiB = 2 MiB。两个文件系统都没有文件,但对于 ext2,df 报告使用了 21 KiB(可能与 this question 有关)。

【讨论】:

以上是关于为啥shutils和df报告的磁盘大小有百分之几的差异?的主要内容,如果未能解决你的问题,请参考以下文章

linux 查看硬盘使用情况

MySQL百分比显示和显示前百分之几的方法

MySQL百分比显示和显示前百分之几的方法

MySQL百分比显示和显示前百分之几的方法

df和du命令—查看磁盘和文件大小

df和du命令—查看磁盘和文件大小