Используемое пространство, рассчитанное statvfs() для файловой системы, больше, чем сумма размеров всех файлов в файловой системе.

У меня есть небольшой раздел размером 50 МБ, отформатированный как ext4, и только с одним каталогом, содержащим набор фотографий, смонтированных в /mnt/tmp.

Затем я использую statvfs() для расчета используемых байтов в разделе и lstat() для расчета размера каждого файла внутри, для этого я написал эту программу:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/statvfs.h>
#include <stdint.h>
#include <string.h>
#include <dirent.h>
#include <stdlib.h>

//The amount of bytes of all files found
uint64_t totalFilesSize=0;

//Size for a sector in the fs
unsigned int sectorSize=0;

void readDir(char *path) {
    DIR *directory;
    struct dirent *d_file;  // a file in *directory

    directory = opendir (path);

    while ((d_file = readdir (directory)) != 0)
    {
        struct stat filestat;
        char *abPath=malloc(1024);
        memset(abPath, 0, 1024);
        strcpy(abPath, path);
        strcat(abPath, "/");
        strcat(abPath, d_file->d_name);

        lstat (abPath, &filestat);

        switch (filestat.st_mode & S_IFMT)
        {
        case S_IFDIR:
        {
            if (strcmp (".", d_file->d_name) && strcmp ("..", d_file->d_name))
            {
                printf("File: %s\nSize: %d\n\n", abPath, filestat.st_size);

                //Add slack space to the final sum
                int slack=sectorSize-(filestat.st_size%sectorSize);

                totalFilesSize+=filestat.st_size+slack;

                readDir(abPath);
            }
            break;
        }
        case S_IFREG:
        {
            printf("File: %s\nSize: %d\n\n", abPath, filestat.st_size);

            //Add slack space to the final sum
            int slack=sectorSize-(filestat.st_size%sectorSize);

            totalFilesSize+=filestat.st_size+slack;

            break;
        }
        }

        free(abPath);
    }

    closedir (directory);
}

int main (int argc, char **argv) {

    if(argc!=2) {
        printf("Error: Missing required parameter.\n");
        return -1;
    }

    struct statvfs info;
    statvfs (argv[1], &info);

    sectorSize=info.f_bsize; //Setting global variable

    uint64_t usedBytes=(info.f_blocks-info.f_bfree)*info.f_bsize;

    readDir(argv[1]);

    printf("Total blocks: %d\nFree blocks: %d\nSize of block: %d\n\
Size in bytes: %d\nTotal Files size: %d\n",
            info.f_blocks, info.f_bfree, info.f_bsize, usedBytes, totalFilesSize);

    return 0;
}

Передавая точку монтирования раздела в качестве параметра (/mnt/tmp), программа показывает следующий вывод:

File: /mnt/tmp/lost+found
Size: 12288

File: /mnt/tmp/photos
Size: 1024

File: /mnt/tmp/photos/IMG_3195.JPG
Size: 2373510

File: /mnt/tmp/photos/IMG_3200.JPG
Size: 2313695

File: /mnt/tmp/photos/IMG_3199.JPG
Size: 2484189

File: /mnt/tmp/photos/IMG_3203.JPG
Size: 2494687

File: /mnt/tmp/photos/IMG_3197.JPG
Size: 2259056

File: /mnt/tmp/photos/IMG_3201.JPG
Size: 2505596

File: /mnt/tmp/photos/IMG_3202.JPG
Size: 2306304

File: /mnt/tmp/photos/IMG_3204.JPG
Size: 2173883

File: /mnt/tmp/photos/IMG_3198.JPG
Size: 2390122

File: /mnt/tmp/photos/IMG_3196.JPG
Size: 2469315

Total blocks: 47249
Free blocks: 19160
Size of block: 1024
Size in bytes: 28763136
Total Files size: 23790592

Обратите внимание на последние две строки. В файловой системе FAT32 количество такое же, а в ext4 другое.

Итак, вопрос: почему?


person jlledom    schedule 31.12.2011    source источник
comment
Из справочной страницы statvfs: не указано, имеют ли все члены возвращаемой структуры значимые значения во всех файловых системах.   -  person fge    schedule 31.12.2011
comment
@fge ой, значит, нет способа получить надежное количество байтов, используемых в fs? Спасибо за ответ.   -  person jlledom    schedule 31.12.2011
comment
Ну, самое близкое, что вы можете сделать, это то, что делает df, и я не знаю, как он вычисляет пространство. Как говорится в ответе ниже, есть также разреженные файлы и т. д. А также рассмотрим случай удаленного, но все еще открытого файла: он займет место на диске, но никогда не будет отображаться в каталоге.   -  person fge    schedule 31.12.2011
comment
связанный: заголовок unix.stackexchange.com/questions/353156/   -  person Ciro Santilli 新疆再教育营六四事件ۍ    schedule 11.03.2018


Ответы (2)


statvfs() — это операция на уровне файловой системы. Используемое пространство будет рассчитываться с точки зрения файловой системы. Поэтому:

  1. Он будет содержать любые структуры файловой системы: для файловых систем, основанных на традиционном дизайне Unix, включая иноды и любые непрямые блоки. .

    В некоторых моих системах у меня обычно есть 256-байтовый индексный дескриптор на 32 КБ пространства для корневого раздела. Меньшие разделы могут иметь еще более высокую плотность инодов, чтобы обеспечить достаточное количество инодов для большого количества файлов — я считаю, что mke2fs по умолчанию — это один инод на 16 КБ пространства.

    Создание файловой системы Ext4 размером 850 МБ с параметрами по умолчанию приводит к файловой системе с примерно 54 000 инодов, которые занимают более 13 МБ пространства.

  2. Для Ext3/Ext4 это также будет включать журнал, минимальный размер которого составляет 1024 блока файловой системы. Для общего размера блока 4 КБ это минимум 4 МБ на файловую систему.

    Файловая система Ext4 размером 850 МБ по умолчанию будет иметь журнал размером 16 МБ.

  3. Результат statvfs() также будет включать любые удаленные, но все еще открытые файлы — это часто происходит с разделами, содержащими tmp каталоги для использования приложениями.

  4. Чтобы увидеть фактическое пространство, используемое файлом с lstat(), вам нужно использовать поле st_blocks структуры stat и умножьте на 512. Судя по размерам, отображаемым в выводе вашей программы, вы используете поле st_size, которое представляет собой точный размер файла в байтах. Как правило, это будет меньше, чем фактическое используемое пространство — файл размером 5 КБ фактически будет использовать 8 КБ в файловой системе с блоками по 4 КБ.

    И наоборот, разреженный файл будет использовать меньше блоков, чем указано его размером файла.

Таким образом, дополнительное использование пространства, упомянутое выше, составит довольно заметные суммы, что объясняет наблюдаемое вами несоответствие.

ИЗМЕНИТЬ:

  1. Я только что заметил, что в вашей программе обрабатывается свободное пространство. Хотя это не рекомендуемый способ подсчета фактического используемого пространства (в отличие от кажущегося), похоже, он работает, так что вы не пропустите место. С другой стороны, вам не хватает места, используемого для корневого каталога файловой системы, хотя это, вероятно, будет только один или два блока :-)

  2. Возможно, вы захотите взглянуть на вывод tune2fs -l /dev/xxx. В нем перечислены несколько соответствующих номеров, включая пространство, зарезервированное для метаданных файловой системы.

Кстати, большую часть функций в вашей программе можно реализовать с помощью df и du:

# du -a --block-size=1 mnt/
2379776 mnt/img0.jpg
3441664 mnt/img1.jpg
2124800 mnt/img2.jpg
12288   mnt/lost+found
7959552 mnt/
# df -B1 mnt/
Filesystem     1B-blocks     Used Available Use% Mounted on
/dev/loop0      50763776 12969984  35172352  27% /tmp/mnt

Между прочим, показанная выше тестовая файловая система Ext4 была создана с использованием параметров mkfs по умолчанию в файле изображения размером 50 МБ. Он имеет размер блока 1024 байта, 12 824 128-байтовых индексных дескриптора, которые занимают 1603 КБ, и журнал из 4096 блоков, который использует 4096 КБ. Еще 199 блоков зарезервированы для таблиц дескрипторов групп, согласно tune2fs.

person thkala    schedule 31.12.2011
comment
Эта программа является доказательством того, что я подготовился для иллюстрации этой задачи, поэтому я не буду использовать ее в реальном мире, где есть лучшие другие варианты для того, чтобы сделать то же самое. Моя настоящая проблема в том, что я пытаюсь сделать индикатор выполнения в другой программе, и он никогда не заканчивается на 100%, когда он просто заканчивает копирование файлов, индикатор останавливается на 85% из-за этой проблемы. - person jlledom; 01.01.2012

Иноды, вероятно, не учитываются и могут содержать небольшие данные.

Если файл разреженный, его размер больше, чем он фактически занят.

Если файл жестко связан более одного раза, общий индексный дескриптор используется совместно.

Документ о Ext4 находится здесь, Кумар и др.

person Basile Starynkevitch    schedule 31.12.2011
comment
Я провел аналогичный тест на жестком диске объемом 800 МБ с 9 разделами, и результат отличается более чем на 40 МБ. Я думаю, что это слишком много для инодов, как вы думаете? В программе исправлена ​​проблема с нехваткой места для мелких файлов. И в этом конкретном примере нет жестких ссылок, только обычные файлы. - person jlledom; 31.12.2011
comment
Это утверждение: если файл разреженный, его размер меньше, чем он фактически занят. является ложным. Верно и обратное. - person Employed Russian; 31.12.2011
comment
@Basile Starynkevitch Отличная ссылка! - person jlledom; 31.12.2011