Почему df и df -h показывают разное значение? Как df -h выполняет расчет?

Почему df и df -h показывают разное значение? Как df -h выполняет расчет?

как именно работает df -h? Если я запущу df, то получу это:

Filesystem     1K-blocks    Used Available Use% Mounted on
/dev/simfs      41943040 7659828  34283212  19% /

Если я запущу df -h, то получу следующее:

Filesystem      Size  Used Avail Use% Mounted on
/dev/simfs       40G  7.4G   33G  19% /

Вопрос в том, как получить те же числа?

41943040 / 1024 / 1024 = 40 Хорошо, давайте разделим остальные на 1024.

7659828 / 1024 / 1024 = 7,304981

Тогда, может быть, к 1000?

7659828 / 1000 / 1000 = 7,659828

Как df -hполучилось 7.4G?

34283212 / 1024 / 1024 = 32,695, which is ±33G

Хотя df имеет открытый исходный код, яклонированныйрепозиторий и проверил код. Вот что я нашел:

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));
    }

У меня нет опыта работы с этим языком, но, как я понимаю, он пытается проверить, делится ли значение в каждом столбце на 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;

Итак, давайте взломаем 7659828 / 1024 / 1024 = 7,304981. -hдал ответ7.4G

7659828 / 1024 = 7480,xxx
7659828 / 1000 = 7659,xxx

в то время как 7659 больше, чем 7480, разделите на 1024.

Все еще большая цифра, давайте продолжим:

7659828 / 1024 / 1024 = 7,xxx  (7,3049..)
7659828 / 1024 / 1000 = 7,xxx  (7,4803..)

сейчас берется 1000 и получается 7,48 и яполагатьгде-то в коде он округляет в меньшую сторону, так что «лучше сказать меньше, чем больше», хотя вы можете поместить 7,4 ГБ данных, но не можете поместить 7,5 ГБ.

Та же история с 33.4G

34283212 / 1024 / 1000 = 33.47...

Итак, он становится 33G.

решение1

Код, который вы разместили, взят из функции "get_header", которая генерирует текст в первой строке. В вашем случае это относится к заголовку "1K-blocks" (вызовите, df -B1023чтобы увидеть разницу).

Важно отметить: «1 КБ» относится к блокам по 1024 байта, а не к блокам по 1000 байт (обозначаемым как «блоки по 1 КБ», см. 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;

Все вычисления производятся с основанием 1024 в удобном для чтения человеком формате ("-h"). В дополнение к показанному human_output_opts, есть настройка по умолчанию, которая применяется здесь (см. human.h, объявление enum):

/* 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,

Поскольку human_output_opts не включает human_round_to_nearest или human_floor, он будет использовать свое значение по умолчанию human_ceiling. Поэтому все вычисленные значения будут округлены в большую сторону.

Для проверки настроек мы можем попробовать рассчитать формат, удобный для чтения человеком, на основе блоков по 1К из 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

Что совпадает с выводом df -h.

(... а если вы предпочитаете 1000-байтовый формат, вы можете просто вызвать df -H).

решение2

Ни dfпрограмма из FreeBSD (отсюда df -hи родом), ни dfреализация из Solaris не ведут себя таким образом.

Поскольку исходные коды Solaris являются OpenSource, вы можете проверить, сможете ли вы скомпилировать их dfна вашей ОС:

Связанный контент