Използваното пространство, изчислено от statvfs() за файлова система, е по-голямо от сумата от размерите на всички файлове във fs

Имам малък дял от 50MiB, форматиран като 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, това включва inodes и всички индиректни блокове.

    В някои от моите системи обикновено имам 256-байтов inode на 32KB пространство за основния дял. По-малките дялове може да имат дори по-висока плътност на inode, за да осигурят достатъчно inode за голям брой файлове - вярвам, че mke2fs по подразбиране е един inode на 16KB пространство.

    Създаването на 850 MB Ext4 файлова система с опциите по подразбиране води до файлова система с около 54 000 inode, които консумират над 13 MB пространство.

  2. За Ext3/Ext4 това също ще включва дневника, който има минимален размер от 1024 блока на файловата система. За общия размер на блока от 4KB това е минимум 4MB на файлова система.

    850 MB Ext4 файлова система ще има 16MB журнал по подразбиране.

  3. Резултатът от statvfs() също ще включва всички изтрити, но все още отворени файлове - това често се случва на дялове, съдържащи tmp директории за използване от приложения.

  4. За да видите действителното пространство, използвано от файл с lstat(), трябва да използвате полето st_blocks от структурата stat и умножете с 512. Съдейки по размерите, показани в изхода на вашата програма, вие използвате полето st_size, което е точният размер на файла в байтове. Това обикновено ще бъде по-малко от реално използваното пространство - файл от 5 KB всъщност ще използва 8 KB във файлова система с блокове от 4 KB.

    Обратно, един разреден файл ще използва по-малко блокове от това, което е посочено от неговия размер на файла.

Като такова, допълнителното използване на пространството, споменато по-горе, ще доведе до доста забележими суми, които обясняват несъответствието, което виждате.

РЕДАКТИРАНЕ:

  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 на 50MB файл с изображение. Той има размер на блока от 1024 байта, 12 824 128-байтови inode, които консумират 1603 KB и дневник от 4096 блока, който използва 4096 KB. Допълнителни 199 блока са запазени за таблиците на груповите дескриптори, според tune2fs.

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

+1 с изключение на частта Common Service Locator. Ако пишете рамка, DI контейнерът по никакъв начин не трябва да фигурира в API, нито пък е необходим: stackoverflow.com/questions/2045904/
person Basile Starynkevitch    schedule 31.12.2011
comment
Направих подобен тест в твърд диск от 800MiB с 9 дяла и резултатът се различава с повече от 40MiB. Мисля, че е твърде много за inodes, мислите ли? Проблемът с празното пространство за малки файлове е коригиран в програмата. И този конкретен пример няма твърди връзки, само обикновени файлове. - person jlledom; 31.12.2011
comment
Това твърдение: Ако файлът е рядък, размерът му е по-малък от действително заетия. е невярно. Точно обратното е. - person Employed Russian; 31.12.2011
comment
@Basile Starynkevitch Страхотна връзка! - person jlledom; 31.12.2011