为什么 df 和 df -h 显示不同的值?df -h 如何进行计算?

Luk*_*sis 5 disk-usage coreutils

df -h 究竟是如何工作的?如果我运行df,我会得到这个:

Filesystem     1K-blocks    Used Available Use% Mounted on
/dev/simfs      41943040 7659828  34283212  19% /
Run Code Online (Sandbox Code Playgroud)

如果我运行df -h,我会得到这个:

Filesystem      Size  Used Avail Use% Mounted on
/dev/simfs       40G  7.4G   33G  19% /
Run Code Online (Sandbox Code Playgroud)

问题是如何获得相同的数字?

41943040 / 1024 / 1024 = 40 好的,让我们将其他人除以 1024。

7659828 / 1024 / 1024 = 7,304981
Run Code Online (Sandbox Code Playgroud)

那么也许到1000?

7659828 / 1000 / 1000 = 7,659828
Run Code Online (Sandbox Code Playgroud)

如何df -h得到7.4G?

34283212 / 1024 / 1024 = 32,695, which is ±33G
Run Code Online (Sandbox Code Playgroud)

虽然 df 是开源的,但我已经克隆了 repo 并检查了代码。这就是我发现的:

for (col = 0; col < ncolumns; col++)
    {
      char *cell = NULL;
      char const *header = _(columns[col]->caption);

      if (columns[col]->field == SIZE_FIELD
          && (header_mode == DEFAULT_MODE
              || (header_mode == OUTPUT_MODE
                  && !(human_output_opts & human_autoscale))))
        {
          char buf[LONGEST_HUMAN_READABLE + 1];

          int opts = (human_suppress_point_zero
                      | human_autoscale | human_SI
                      | (human_output_opts
                         & (human_group_digits | human_base_1024 | human_B)));

          /* Prefer the base that makes the human-readable value more exact,
             if there is a difference.  */

          uintmax_t q1000 = output_block_size;
          uintmax_t q1024 = output_block_size;
          bool divisible_by_1000;
          bool divisible_by_1024;

          do
            {
              divisible_by_1000 = q1000 % 1000 == 0;  q1000 /= 1000;
              divisible_by_1024 = q1024 % 1024 == 0;  q1024 /= 1024;
            }
          while (divisible_by_1000 & divisible_by_1024);

          if (divisible_by_1000 < divisible_by_1024)
            opts |= human_base_1024;
          if (divisible_by_1024 < divisible_by_1000)
            opts &= ~human_base_1024;
          if (! (opts & human_base_1024))
            opts |= human_B;

          char *num = human_readable (output_block_size, buf, opts, 1, 1);

          /* Reset the header back to the default in OUTPUT_MODE.  */
          header = _("blocks");

          /* TRANSLATORS: this is the "1K-blocks" header in "df" output.  */
          if (asprintf (&cell, _("%s-%s"), num, header) == -1)
            cell = NULL;
        }
      else if (header_mode == POSIX_MODE && columns[col]->field == SIZE_FIELD)
        {
          char buf[INT_BUFSIZE_BOUND (uintmax_t)];
          char *num = umaxtostr (output_block_size, buf);

          /* TRANSLATORS: this is the "1024-blocks" header in "df -P".  */
          if (asprintf (&cell, _("%s-%s"), num, header) == -1)
            cell = NULL;
        }
      else
        cell = strdup (header);

      if (!cell)
        xalloc_die ();

      hide_problematic_chars (cell);

      table[nrows - 1][col] = cell;

      columns[col]->width = MAX (columns[col]->width, mbswidth (cell, 0));
    }
Run Code Online (Sandbox Code Playgroud)

我没有使用这种语言的经验,但据我所知,它会尝试检查每列上的值是否可以被 1024 或 1000 整除,然后选择更好的值来呈现-h选项的值。但是无论我除以 1000 还是 1024 都得不到相同的值。为什么?

我想我知道为什么。它检查在每个分区上除以 1000 或 1024 。

          if (divisible_by_1000 < divisible_by_1024)
            opts |= human_base_1024;
          if (divisible_by_1024 < divisible_by_1000)
            opts &= ~human_base_1024;
          if (! (opts & human_base_1024))
            opts |= human_B;
Run Code Online (Sandbox Code Playgroud)

所以让我们破解 7659828 / 1024 / 1024 = 7,304981。-h给出了7.4G 的答案

7659828 / 1024 = 7480,xxx
7659828 / 1000 = 7659,xxx
Run Code Online (Sandbox Code Playgroud)

而 7659 大于 7480,则除以 1024。

还是个大数目,我们继续:

7659828 / 1024 / 1024 = 7,xxx  (7,3049..)
7659828 / 1024 / 1000 = 7,xxx  (7,4803..)
Run Code Online (Sandbox Code Playgroud)

现在需要 1000 并给出 7,48,我相信它在代码中的某个地方四舍五入,所以“最好说少而不是多”,而您可以放入 7.4G 的数据,但不能放入 7.5G。

与 33.4G 相同的故事

34283212 / 1024 / 1000 = 33.47...
Run Code Online (Sandbox Code Playgroud)

所以变成了33G。

小智 5

您发布的代码来自函数“get_header”,它在第一行生成文本。在您的情况下,这适用于标题“1K-blocks”(致电df -B1023查看差异)。

需要注意的重要事项:“1K”指的是 1024 字节的块,而不是 1000 字节的块(由“1kB 块”表示,请参阅 参考资料df -B1000

人类可读格式的数字计算由函数“human_readable”(human.c:153)处理。在 df.c:1571 中,您可以找到使用-h标志调用时使用的选项:

case 'h':
    human_output_opts = human_autoscale | human_SI | human_base_1024;
    output_block_size = 1;
    break;
Run Code Online (Sandbox Code Playgroud)

所有计算均以人类可读格式(“-h”)以 1024 为基数完成。除了显示的 human_output_opts 之外,还有一个适用于此处的默认设置(参见 human.h,枚举声明):

/* The following three options are mutually exclusive.  */
/* Round to plus infinity (default).  */
human_ceiling = 0,
/* Round to nearest, ties to even.  */
human_round_to_nearest = 1,
/* Round to minus infinity.  */
human_floor = 2,
Run Code Online (Sandbox Code Playgroud)

由于human_output_opts 不包括human_round_to_nearest 或human_floor,它将使用其默认值human_ceiling。因此,所有计算值都将四舍五入。

为了验证设置,我们可以尝试根据以下 1K 块计算人类可读的格式df

Size = ceil(41943040/1024/1024) = ceil(40) = 40
Used = ceil(7659828/1024/1024) = ceil(7.305) = 7.4
Available = ceil(34283212/1024/1024) = ceil(32.695) = 33
Run Code Online (Sandbox Code Playgroud)

这与 的输出相同df -h

(...如果您更喜欢 1000 字节格式,您可以简单地调用df -H)。