Правильно преобразовывать statvfs в проценты
У меня есть ужасно несложная тестовая программа, которая выводит следующие цифры.
то есть.
int main(int argc, char* argv[])
struct statvfs vfs;
statvfs(argv[1], &vfs);
printf("f_bsize (block size): %lu\n"
"f_frsize (fragment size): %lu\n"
"f_blocks (size of fs in f_frsize units): %lu\n"
"f_bfree (free blocks): %lu\n"
"f_bavail free blocks for unprivileged users): %lu\n"
"f_files (inodes): %lu\n"
"f_ffree (free inodes): %lu\n"
"f_favail (free inodes for unprivileged users): %lu\n"
"f_fsid (file system ID): %lu\n"
"f_flag (mount flags): %lu\n"
"f_namemax (maximum filename length)%lu\n",
vfs.f_bsize,
vfs.f_frsize,
vfs.f_blocks,
vfs.f_bfree,
vfs.f_bavail,
vfs.f_files,
vfs.f_ffree,
vfs.f_favail,
vfs.f_fsid,
vfs.f_flag,
vfs.f_namemax);
return 0;
}
Распечатывает:
f_bsize (block size): 4096
f_frsize (fragment size): 4096
f_blocks (size of fs in f_frsize units): 10534466
f_bfree (free blocks): 6994546
f_bavail free blocks for unprivileged users): 6459417
f_files (inodes): 2678784
f_ffree (free inodes): 2402069
f_favail (free inodes for unprivileged users): 2402069
f_fsid (file system ID): 12719298601114463092
f_flag (mount flags): 4096
f_namemax (maximum filename length)255
df печатает для корня fs:
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/sda5 42137864 14159676 25837672 36% /
Но здесь я смущен.
25837672 + 14159676!= 42137846 (фактически 39997348)
Поэтому, если бы я должен был выполнить вычисление 14159676/42137864 * 100, я получаю 33% не 36% в качестве отпечатков df.
Но если я calc
14159676/39997348 * 100 Я получаю 35%.
Почему все расхождения и где df получает номер 42137864? Связано ли это с некоторым преобразованием в 1k блоки против фактического размера системного блока, который равен 4k?
Это будет интегрировано в мое приложение для кеширования, чтобы сообщить мне, когда диск находится на некотором пороге... например. 90%, прежде чем я начну освобождать блоки фиксированного размера, размер которых равен 2 ^ n.
Итак, что мне нужно, это функция, которая дает мне достаточно точный%.
EDIT:
Теперь я могу сопоставить то, что печатает df. За исключением% Used. Это заставляет нас задаться вопросом, насколько точно это все. Каков размер фрагмента?
unsigned long total = vfs.f_blocks * vfs.f_frsize / 1024;
unsigned long available = vfs.f_bavail * vfs.f_frsize / 1024;
unsigned long free = vfs.f_bfree * vfs.f_frsize / 1024;
printf("Total: %luK\n", total);
printf("Available: %luK\n", available);
printf("Used: %luK\n", total - free);
EDIT2:
unsigned long total = vfs.f_blocks * vfs.f_frsize / 1024;
unsigned long available = vfs.f_bavail * vfs.f_frsize / 1024;
unsigned long free = vfs.f_bfree * vfs.f_frsize / 1024;
unsigned long used = total - free;
printf("Total: %luK\n", total);
printf("Available: %luK\n", available);
printf("Used: %luK\n", used);
printf("Free: %luK\n", free);
// Calculate % used based on f_bavail not f_bfree. This is still giving out a different answer to df???
printf("Use%%: %f%%\n", (vfs.f_blocks - vfs.f_bavail) / (double)(vfs.f_blocks) * 100.0);
f_bsize (block size): 4096
f_frsize (fragment size): 4096
f_blocks (size of fs in f_frsize units): 10534466
f_bfree (free blocks): 6994182
f_bavail (free blocks for unprivileged users): 6459053
f_files (inodes): 2678784
f_ffree (free inodes): 2402056
f_favail (free inodes for unprivileged users): 2402056
f_fsid (file system ID): 12719298601114463092
f_flag (mount flags): 4096
f_namemax (maximum filename length)255
Total: 42137864K
Available: 25836212K
Used: 14161136K
Free: 27976728K
Use%: 38.686470%
[email protected]:~/dev$ df
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/sda5 42137864 14161136 25836212 36% /
Я получаю 38%, а не 36. Если рассчитывается f_bfree, я получаю 33%. Является ли df неправильным или это просто никогда не будет точным? Если это так, то я хочу опираться на сторону консервативности.
Ответы
Ответ 1
df
данные могут быть основаны на f_bavail
, а не f_bfree
. Вам может быть полезно посмотреть исходный код на df, чтобы увидеть, как он это делает. Он имеет ряд крайних случаев, с которыми ему приходится иметь дело (например, когда используемое пространство превышает объем пространства, доступного для пользователей без полномочий root), но соответствующий код для нормального случая находится здесь:
uintmax_t u100 = used * 100;
uintmax_t nonroot_total = used + available;
pct = u100 / nonroot_total + (u100 % nonroot_total != 0);
Другими словами, 100 * used / (used + available)
округляется. Вставка значений из вашего вывода df дает 100 * 14159676 / (14159676 + 25837672) = 35.4015371
, который округляется до 36%, как и вычисляемый df
.
Ответ 2
Это самое близкое, что мне нужно для соответствия вывода df -h
для используемого процента:
const uint GB = (1024 * 1024) * 1024;
struct statvfs buffer;
int ret = statvfs(diskMountPoint.c_str(), &buffer);
const double total = ceil((double)(buffer.f_blocks * buffer.f_frsize) / GB);
const double available = ceil((double)(buffer.f_bfree * buffer.f_frsize) / GB);
const double used = total - available;
const double usedPercentage = ceil((double)(used / total) * (double)100);
return usedPercentage;
Ответ 3
В вашем Редактировании №2 для вычисления результата необходимо выполнить следующее:
100.0 * (double) (vfs.f_blocks - vfs.f_bfree) / (double) (vfs.f_blocks - vfs.f_bfree + vfs.f_bavail)
Обоснование:
Используется = f_blocks - f_bfree
Свободно = f_bavail
df% = Используется/(Используется + Свободно)
Ответ 4
Показатели statvfs несколько путают. Вы можете использовать исходный код psutil в качестве примера того, как получить значащие значения в байтах: https://github.com/giampaolo/psutil/blob/f4734c80203023458cb05b1499db611ed4916af2/psutil/_psposix.py#L119