获取Linux中包含500万个文件的目录所占用的总空间的更快方法

Posted

技术标签:

【中文标题】获取Linux中包含500万个文件的目录所占用的总空间的更快方法【英文标题】:Faster way to get the total space taken by the directory containing 5 million files in linux 【发布时间】:2019-11-16 08:59:33 【问题描述】:

我有一个运行 linux 的目标板,它的目录中有大约 500 万多个文件。 (此目录没有任何子目录) 如果我执行这个程序,需要几分钟才能获得总空间信息。 有没有更快的方法来实现这一点?谢谢

#include <stdio.h>
#include <dirent.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <sys/stat.h>
#include <errno.h>


void calcSpace(char *path,  long long int *totalSpace) 

    DIR *dir;                /* dir structure we are reading */
    struct dirent *ent;      /* directory entry currently being processed */
    char absPath[200];
    struct stat statbuf;     /* buffer for stat()*/
    long long int fileCount=0;

    fprintf(stderr, "Opening dir %s\n", path);
    dir = opendir(path);
    if(NULL == dir) 
        perror(path);
        return;
    
    while((ent = readdir(dir))) 
    
       fileCount++;
       sprintf(absPath, "%s/%s", path, ent->d_name);
       if(stat(absPath, &statbuf)) 
          perror(absPath);
          return;
       
       *totalSpace= (*totalSpace) + statbuf.st_size;
    

    fprintf(stderr, "Closing dir %s\n", path);
    printf("fileCount=%lld.\n", fileCount);
    closedir(dir);


int main(int argc, char *argv[]) 

    char *dir;
    long long int totalSpace=0;
    if(argc > 1)
        dir = argv[1];
    else
        dir = ".";

    calcSpace(dir,  &totalSpace);
    printf("totalSpace=%lld\n", totalSpace);
    return 0;

【问题讨论】:

更换硬盘?使用内存盘?使用数据库? 你可以 chdir 到目标目录来避免大量的字符串操作,但这可能不会给你带来太多好处。 也许您可以使用ftw 来避免额外调用stat。避免在每次迭代中取消引用 *totalSpace 也会有所帮助 不幸的是dirent 不包含文件大小,即使readdir 读取包含文件大小的目录条目。所以没有办法阻止对stat 的额外调用。除非您修改 readdirdirent 以包含文件大小,否则这是可能的,因为这是一个可能有自己的库实现的嵌入式板。 如何为这个目录创建一个单独的文件系统,并从它的当前位置符号链接到它?然后你可以向文件系统询问使用的空间。 【参考方案1】:

正如 cmets 中所述,主要成本似乎是对 statreaddir 的调用。

优化readdir 调用

我们可以使用getdents(2) 系统调用来节省readdir 成本上的一些严重成本。此系统调用类似于“readdir”,但您可以使用它来读取每个系统调用中的多个目录条目 - 大大减少为每个条目调用 readdir 系统调用的开销。

可以在我链接到的man 页面中找到代码示例。需要注意的重要一点是,您可能应该使用getdentscount 参数 - 在示例中为 1024)来调整一次读取的条目数量,以找到适合您的配置和机器的最佳位置(与readdir 相比,此步骤可能会给您带来您想要的性能提升)。

优化stat 调用

建议使用fstatat(2) 函数而不是您使用的常规stat(两者都使用relevant man page)。这是因为fstatat(2) 的第一个参数是dirfd - 您所声明的文件所在目录的文件描述符。

这意味着您可以打开目录的文件描述符一次(使用open(2)),然后所有fstatat 调用都将使用此dirfd 完成。这将优化内核中的声明过程(作为整个路径和目录本身的引用,不应再为每个stat syscall 解析),并且可能会使您的代码更简单,更快一点(作为路径不再需要串联)。

【讨论】:

我不相信从 readdir 切换到直接调用 getdents 在这里会有所作为。 Readdir 是通过为多个条目发出 getdents 来实现的,主要成本是实际的 IO。较大块的影响很小 同样,在当前文件夹中使用 fstatat 而不是 stat 是最少的。 fstat 的主要开销是在 5M 文件夹内搜索文件名。定位当前文件夹是一个小组件,很可能在进程中被缓存。 readdir/getdents 和 stat/fstatat 性能的基准不支持切换的情况(在我的 Mint 19 上)。在 OP 使用的嵌入式系统上可能会有所不同。【参考方案2】:

虽然从技术上讲这不是一个“答案”,但这里有一些关于这个问题的问题:

在幕后,大多数 Linux 文件系统将文件表示为“inode”,目录表示名称列表->inode。根据文件系统的不同,列表可能是简单的线性列表、平衡树或散列 - 这将提高查找性能,以及处理包含大量文件的文件夹时的损失。

较旧的操作系统(Vax VMS,以及它的前身 FILES-11)除了按文件路径打开文件外,还必须能够通过唯一 ID(文件编号、序列号)打开文件。在 Linux 空间中,这相当于按 inode 号打开文件。这种方法可以以很少的开销打开或查询元数据。不幸的是,Unix/Linux 在应用程序级别没有类似的功能,据我所知,没有计划创建这样的接口。这将需要对所有文件系统驱动程序进行重大升级。

另一种方法是实现多文件stat 系统调用,这将受益于能够在一次扫描期间对所有文件执行文件查找。虽然系统调用的类型会加速各种实用程序,但它不会使大多数应用程序受益,这些应用程序通常会坚持使用 POSIX 调用。如果不更改各种文件系统驱动程序,将很难实现这样的功能。我相信它不太可能很快推出。

【讨论】:

以上是关于获取Linux中包含500万个文件的目录所占用的总空间的更快方法的主要内容,如果未能解决你的问题,请参考以下文章

linux下如何查找当前目录下所有jar包中包含AAA内容的class文件名

php PHP:获取我的images目录中包含的所有文件名列表[重复]

Linux命令干货分享

EduCoder Linux之文件打包和解压缩

基于固定相对路径在 zip 文件中包含目录结构

linux下使用 du查看某个文件或目录占用磁盘空间的大小