由statvfs()为文件系统计算的已用空间大于fs中所有文件的大小总和

jll*_*dom 5 c linux filesystems

我有一个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;
}
Run Code Online (Sandbox Code Playgroud)

将分区的挂载点作为参数(/ 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
Run Code Online (Sandbox Code Playgroud)

请注意最后两行.在FAT32文件系统中,数量相同,但在ext4中有所不同.

所以问题是:为什么?

thk*_*ala 5

statvfs()是一个文件系统级操作.使用的空间将从文件系统的角度计算.因此:

  1. 它将包含任何文件系统结构:对于基于Unix的传统设计的文件系统,包括inode和任何间接块.

    在我的一些系统中,对于根分区,每32KB空间通常有一个256字节的inode.较小的分区可能具有更高的inode密度,为大量文件提供足够的inode - 我相信mke2fs默认是每16KB空间一个inode.

    使用默认选项创建850 MB Ext4文件系统会导致文件系统中包含大约54,000个inode,占用的空间超过13MB.

  2. 对于Ext3/Ext4,它还包括日志,其最小大小为1024个文件系统块.对于4KB的公共块大小,每个文件系统至少为4MB .

    默认情况下,850 MB Ext4文件系统将具有16MB日志.

  3. 结果statvfs()将包括任何已删除但仍然打开的文件 - 这通常发生在包含tmp应用程序使用目录的分区上.

  4. 要查看文件使用的实际空间lstat(),需要使用结构st_blocks字段stat并乘以512.根据程序输出中显示的大小判断,您使用的st_size字段是精确的文件大小(以字节为单位).这通常小于实际使用的空间 - 在具有4KB块的文件系统上,5KB文件实际上将使用8KB.

    相反,稀疏文件将使用比其文件大小指示的更少的块.

因此,上面提到的额外空间使用量将累计到相当明显的数量,这可以解释您所看到的差异.

编辑:

  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
Run Code Online (Sandbox Code Playgroud)

顺便提一下,上面显示的Ext4测试文件系统是使用mkfs50MB图像文件上的默认选项创建的.它的块大小为1,024字节,12,824个128字节的inode,消耗1,603 KB,4096块的日志使用4,096KB.根据以下内容,为组描述符表保留了另外199个块tune2fs.