为什么 shutils 和 df 报告的磁盘大小有几个百分比的差异?
Why is there a few % difference in disk size reported by shutils and df?
我正在编写一个简单的监控脚本,我想向其中添加磁盘 space 检查。但是我发现报告的免费 space 在系统 df
和 shutils.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%。它来自哪里,哪个结果是正确的?
曾经的 1Gb 是 1024 兆字节,但是制造商发现了一个营销技巧,将 50000 兆字节称为 50 兆字节。
所以不同之处在于这些软件实现如何处理这些 Mega,无论是 1000 还是 1024。
Python 似乎有正确的结果。
默认情况下,[man7]: DF(1) (man df
) 在 1 KiB 块中显示数字(大小)。但是,鉴于该操作(除以 1024)同时应用于除法器和除数(计算百分比时),它会自行减少,因此它应该与最终结果没有任何关系。
示例(对于某个 dir):
- 运行 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) , 因此其基础规模将大于报告的规模
正如 已经指出的那样,两种工具的比率 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
source code of coreutils' df
implementation sheds some light on this issue. The 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
包括 space,这是为 meta-data 保留的,例如哪个文件驻留在文件系统中的位置(取决于文件系统,这可能包括脂肪表、索引节点等) .由于您不能将 space 用于常规文件,因此 space 通过使用 (used + free)
而被排除在 Use%
中,而不包括 meta-data.
然而,一项测试表明……
这不是完整的故事。以下脚本在 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 有关)。
我正在编写一个简单的监控脚本,我想向其中添加磁盘 space 检查。但是我发现报告的免费 space 在系统 df
和 shutils.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%。它来自哪里,哪个结果是正确的?
曾经的 1Gb 是 1024 兆字节,但是制造商发现了一个营销技巧,将 50000 兆字节称为 50 兆字节。
所以不同之处在于这些软件实现如何处理这些 Mega,无论是 1000 还是 1024。
Python 似乎有正确的结果。
默认情况下,[man7]: DF(1) (man df
) 在 1 KiB 块中显示数字(大小)。但是,鉴于该操作(除以 1024)同时应用于除法器和除数(计算百分比时),它会自行减少,因此它应该与最终结果没有任何关系。
示例(对于某个 dir):
- 运行 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) , 因此其基础规模将大于报告的规模
正如 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
source code of coreutils' df
implementation sheds some light on this issue. The 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
包括 space,这是为 meta-data 保留的,例如哪个文件驻留在文件系统中的位置(取决于文件系统,这可能包括脂肪表、索引节点等) .由于您不能将 space 用于常规文件,因此 space 通过使用 (used + free)
而被排除在 Use%
中,而不包括 meta-data.
然而,一项测试表明……
这不是完整的故事。以下脚本在 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 有关)。