como exatamente df -h funciona? Se eu executar df
, recebo isto:
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/simfs 41943040 7659828 34283212 19% /
Se eu executar df -h
, recebo isto:
Filesystem Size Used Avail Use% Mounted on
/dev/simfs 40G 7.4G 33G 19% /
A questão é como obter os mesmos números?
41943040 / 1024 / 1024 = 40 OK, vamos dividir os outros por 1024.
7659828 / 1024 / 1024 = 7,304981
Então talvez por 1000?
7659828 / 1000 / 1000 = 7,659828
Como df -h
conseguiu 7.4G?
34283212 / 1024 / 1024 = 32,695, which is ±33G
Embora o df seja de código aberto, clonei o repositório e verifiquei o código. Isso é o que eu encontrei:
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));
}
Não tenho experiência com essa linguagem, mas pelo que entendi, ela tenta verificar se o valor de cada coluna é divisível por 1024 ou 1000 e escolhe o que for melhor para renderizar valores para a -h
opção. Mas não obtenho o mesmo valor, não importa se divido por 1000 ou 1024. Por quê?
Acho que sei por quê. Ele verifica a divisão por 1000 ou 1024 em cada divisão.
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;
então vamos quebrar 7659828/1024/1024 = 7,304981. -h
deu resposta de 7.4G
7659828 / 1024 = 7480,xxx
7659828 / 1000 = 7659,xxx
enquanto 7659 é maior que 7480, divida por 1024.
Ainda é um grande número, vamos continuar:
7659828 / 1024 / 1024 = 7,xxx (7,3049..)
7659828 / 1024 / 1000 = 7,xxx (7,4803..)
leva 1000 agora e dá 7,48 e acredito que em algum lugar do código ele arredonda para baixo, então "melhor dizer menos do que mais" enquanto você pode colocar 7,4 G de dados, mas não pode colocar 7,5 G.
Mesma história com 33.4G
34283212 / 1024 / 1000 = 33.47...
Portanto, torna-se 33G.
O código que você postou é da função "get_header" que gera o texto da primeira linha. No seu caso, isso se aplica ao título "1K-blocks" (ligue
df -B1023
para ver a diferença).Nota importante: "1K" refere-se a blocos de 1024 bytes, não a blocos de 1000 bytes (indicado por "1kB-blocks", consulte
df -B1000
)O cálculo dos números no formato legível por humanos é tratado pela função "human_readable" (human.c:153). Em df.c:1571 você pode encontrar as opções que são usadas quando chamadas com o
-h
sinalizador:Todos os cálculos são feitos com base 1024 em formato legível por humanos ("-h"). Além dos human_output_opts mostrados, há uma configuração padrão que se aplica aqui (consulte human.h, declaração de enum):
Como human_output_opts não inclui human_round_to_nearest ou human_floor, ele usará seu valor padrão de human_ceiling. Todos os valores calculados serão, portanto, arredondados.
Para verificar as configurações, podemos tentar calcular o formato legível por humanos com base nos blocos de 1K de
df
:Que é o mesmo que a saída de
df -h
.(... e se você preferir o formato de 1000 bytes, pode simplesmente chamar
df -H
).Nem o
df
programa do FreeBSD (de ondedf -h
vem originalmente) nem adf
implementação do Solaris se comportam dessa maneira.Como as fontes do Solaris são OpenSource, você pode verificar se pode compilar
df
em seu sistema operacional: