Blog·Tanky WooABOUTRSS

关于 df 命令的几个问题

28 Jan 2014

主要围绕三个问题:

最近发现有个机器的某个分区, 在 df -h 下看使用情况, 显示已使用和总大小还差几个G, 却显示使用率是100%, 且 Used + Avail != Size

拿本机说明:

tankywoo@gentoo-jl::~/ » df -h
Filesystem                   Size  Used Avail Use% Mounted on
/dev/sda3                     15G  5.9G  8.2G  42% /

tankywoo@gentoo-jl::~/ » df
Filesystem                  1K-blocks    Used Available Use% Mounted on
/dev/sda3                    15481840 6163320   8532088  42% /

上面一个是以 Human Readable 的方式来显示的, 下面是以 1K-blocks的方式显示。

看第一个: 5.9 + 8.2 < 15 , 虽然换算成可读方式会有误差, 但是差的也太多了, 明显有问题。

于是网上搜了下, 才知道有 Reserved Space 这个东西。

使用 tune2fs 可以看到:

tankywoo@gentoo-jl::~/ » sudo tune2fs -l /dev/sda3
tune2fs 1.42 (29-Nov-2011)
    Block count:              3932160
    Reserved block count:     196608
    Free blocks:              1392744
    Block size:               4096

注意 Reserved block count, 这个就是保留的空间。

man tune2fs 里有解释:

-m reserved-blocks-percentage

Set the percentage of the filesystem which may only be allocated by privileged processes. Reserving some number of filesystem blocks for use by privileged processes is done to avoid filesystem fragmentation, and to allow system daemons, such as syslogd(8), to continue to function correctly after non-privileged processes are prevented from writing to the filesystem. Normally, the default percentage of reserved blocks is 5%.

当文件系统不可写, 比如空间满了, 这些预留的空间就可以保证特权用户(root)进程正常执行。一般会预留 5% 的blocks。

(15481840 - 6163320 - 8532088) / 15481840 ≈ 5%

计算可以看到, 确实是5%。

当然这个是针对 ext2 / ext3 / ext4 的。参考

不过就像 man 手册里说的, tune2fs -m 可以修改这个大小。

比如:

# 预留 1% 的空间
tune2fs -m 1 /dev/sda1
# 关闭预留空间
tune2fs -m 0 /dev/sda1

# 实际操作
tankywoo@gentoo-jl::~/ » sudo tune2fs -l /dev/sda3 | grep 'Reserved block count'
Reserved block count:     196608
tankywoo@gentoo-jl::~/ » sudo tune2fs -m 1 /dev/sda3
tune2fs 1.42 (29-Nov-2011)
Setting reserved blocks percentage to 1% (39321 blocks)
tankywoo@gentoo-jl::~/ » sudo tune2fs -l /dev/sda3 | grep 'Reserved block count'
Reserved block count:     39321

接着就是另外两个问题了, 前面提到了 1K-blocks, 但是它的大小和单位是多少, 我找了半天没找到。

man df 只有这句话:

Disk space is shown in 1K blocks by default, unless the environment variable POSIXLY_CORRECT is set, in which case 512-byte blocks are used.

后来去 Super User 上求助, 感谢Sami Laine 直接拿出源码说明。

1450   if (human_output_opts == -1)
1451     {
1452       if (posix_format)
1453         {
1454           human_output_opts = 0;
1455           output_block_size = (getenv ("POSIXLY_CORRECT") ? 512 : 1024);
1456         }
1457       else
1458         human_options (getenv ("DF_BLOCK_SIZE"),
1459                        &human_output_opts, &output_block_size);
1460     }

配合man手册可以看到, 在设置了 POSIXLY_CORRECT 环境变量后, 一个 block 是 512-byte, 没有设置则是默认的 1K blocks, 即一个block 是 1024-byte。

15481840(blocks) * 1024(B)/blocks / 1024 / 1024 / 1024 = 14.76G(约15G)

最后一个问题, 如何计算出 Used%, 即使用率。

因为知道了既然有Reserved Space, 那么我先猜测使用率是 Used/Size, 但是这样算出来是约等于39%, 如果加上5%的预留空间, 则又44%了, 始终不是42%。

和上面那个问题一起问的, 也给出了源码:

 799   bv->used = UINTMAX_MAX;
 800   bv->negate_used = false;
 801   if (known_value (bv->total) && known_value (bv->available_to_root))
 802     {
 803       bv->used = bv->total - bv->available_to_root;
 804       bv->negate_used = (bv->total < bv->available_to_root);
 805     }
 806 }

不过显然这段代码只说明了 used是如何计算的, 但是并没有使用率的计算, 于是我自己下载了一份 coreutils 的源码, 版本8.22。

主要涉及到 src/df.clib/fsusage.h 这两个文件。

可以看到:

 133 /* Displayable fields.  */
 134 typedef enum
 135 {
 136   SOURCE_FIELD, /* file system */
 137   FSTYPE_FIELD, /* FS type */
 138   SIZE_FIELD,   /* FS size */
 139   USED_FIELD,   /* FS size used  */
 140   AVAIL_FIELD,  /* FS size available */
 141   PCENT_FIELD,  /* percent used */
 142   ITOTAL_FIELD, /* inode total */
 143   IUSED_FIELD,  /* inodes used */
 144   IAVAIL_FIELD, /* inodes available */
 145   IPCENT_FIELD, /* inodes used in percent */
 146   TARGET_FIELD, /* mount point */
 147   FILE_FIELD    /* specified file name */
 148 } display_field_t;

percent used 使用enum类型的PCENT_FILED 表示。继续搜索这个字段。

 968         case PCENT_FIELD:
 969         case IPCENT_FIELD:
 970           {
 971             double pct = -1;
 972             if (! known_value (v->used) || ! known_value (v->available))
 973               ;
 974             else if (!v->negate_used
 975                      && v->used <= TYPE_MAXIMUM (uintmax_t) / 100
 976                      && v->used + v->available != 0
 977                      && (v->used + v->available < v->used)
 978                      == v->negate_available)
 979               {
 980                 uintmax_t u100 = v->used * 100;
 981                 uintmax_t nonroot_total = v->used + v->available;
 982                 pct = u100 / nonroot_total + (u100 % nonroot_total != 0);
 983               }
 984             else

982行显示了这个比例是如何求出的。 nonroot_total 是减去 reserved space 后的大小, 也就是这个比例是 used / (total - reserved) 求出来的, 并且只要不是整除, 都会加1。

另外其余部分代码时, 也会看到 free 和 avail, free 是总空闲空间, 也就是 avail + reserved = free。这也是网上有些人说用 gparted 可以看到 free 空间大小。

这几个问题算是之前困扰我有一段时间了, 也一直没抽时间去解决, 现在终于解决了.

Man doc -> Google -> Source Code -> Ask for help

以后有问题如果手册和网上没有, 有能力的话还是应该直接去分析源码了, 实在不行再提问。