获取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
的额外调用。除非您修改 readdir
和 dirent
以包含文件大小,否则这是可能的,因为这是一个可能有自己的库实现的嵌入式板。
如何为这个目录创建一个单独的文件系统,并从它的当前位置符号链接到它?然后你可以向文件系统询问使用的空间。
【参考方案1】:
正如 cmets 中所述,主要成本似乎是对 stat
和 readdir
的调用。
优化readdir
调用
我们可以使用getdents(2)
系统调用来节省readdir
成本上的一些严重成本。此系统调用类似于“readdir”,但您可以使用它来读取每个系统调用中的多个目录条目 - 大大减少为每个条目调用 readdir
系统调用的开销。
可以在我链接到的man
页面中找到代码示例。需要注意的重要一点是,您可能应该使用getdents
(count
参数 - 在示例中为 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文件名