在不知道文件描述符的情况下检测文件大小不为零的最快方法是啥?

Posted

技术标签:

【中文标题】在不知道文件描述符的情况下检测文件大小不为零的最快方法是啥?【英文标题】:What is the fastest way to detect file size is not zero without knowing the file descriptor?在不知道文件描述符的情况下检测文件大小不为零的最快方法是什么? 【发布时间】:2019-06-23 16:35:49 【问题描述】:

为了简要解释我为什么需要这个, 我目前正在通过stat(2) 进行检测。我无法控制文件描述符(可能会被其他线程用完,因为我的代码被注入以替换系统调用),所以我不能使用fstat(2)(更快)。我需要做这个检查很多次,那么有没有更快的方法来做同样的事情? 我在没有父子关系的不同进程中检查同一个文件。

【问题讨论】:

我认为这是在 Linux 上,但为了清楚起见,您应该将其添加到您的问题中。 为什么一定要快?此外:鉴于 stat stat() 是单个系统调用,没有办法做得更好。 您是否有性能问题,或者您正在做一些过早的优化? 我需要多次执行此检查,那么是否有更快的方法来执行相同的操作? 听起来像 a TOCTOU bug 和 an XY problem开机。什么实际的问题是不断检查应该解决的文件大小? 真正的问题是:您的程序将根据这些信息做什么?如果文件的大小==0,会发生什么?如果它的大小> 0,会发生什么? [另请参阅:Andrews TOCTOU-link] 奖励:也许您可以使用 inotify? 【参考方案1】:

您可能应该为自己进行基准测试。

我测量过

//Real-time System-time
272.58 ns(R)    170.11 ns(S)  //lseek
366.44 ns(R)    366.28 ns(S)  //fstat
812.77 ns(R)    711.69 ns(S)  //stat("/etc/profile",&sb)

在我的 Linux 笔记本电脑上。它在运行之间会有一点波动,但lseek 通常比fstat 快​​一堆 ns,但你还需要一个 fd 并且opening 在大约 1.6µs 时相当昂贵,所以stat 可能是适合您情况的最佳选择。


正如tom-karzes 所指出的,stat 应该取决于路径中目录组件的数量。我在 PATH_MAX 长的 "/foo/foo/.../foo" 目录上尝试过,我得到了关于 80µs 的信息。

【讨论】:

你是如何测量的? @Serge 在循环中运行了很多次,由clock_gettime 和getrusage 报告的不同时间除以迭代次数。 我怀疑stat 的速度很大程度上取决于路径中目录组件的数量,而fstat 则不受深度的影响。当然,文件系统缓存无疑有助于stat @TomKarzes :在 lseek,fstat 的情况下,open() 调用将已经完成所有目录和文件系统缓存。 @TomKarzes 确实如此。我已经在“/etc/profile”上测试了统计信息。再次尝试“foo/foo/foo.../foo” PATH_MAX long(适合 4096B 的内容),我得到了 79.15 µs。【参考方案2】:

知道您正在搜索的文件系统的最有效方法是打开关联的块设备并搜索(逐块)inode表,并从那里的inode检查实际大小(打开块设备,所以您从内存映像中获取 inode,而不是从磁盘中获取)。这允许您以快速而肮脏的方式获取文件系统的所有零长度 inode。缺点是你首先需要获取文件系统的信息,然后才能直接访问块设备,这对于非root进程通常是禁止的。之后,您必须搜索文件系统以获取所涉及文件的名称,以防万一您需要对这些文件执行某些操作。

顺便说一下,您对 不能在与另一个线程的共享文件描述符上使用 fstat(2) 的假设是错误的,因为 stat 系统调用对打开的文件描述符进行操作,并且不会'不对文件做任何事情——它是非阻塞的——并且系统保证在访问 stat 结构时 inode 被锁定。

在这种情况下使用lseek(2)的方法是无效的,因为它实际上是把文件指针移动到文件末尾,然后又回到保存的地方,而这需要两个系统调用来做和撤消移动,如果另一个线程使用另一个系统调用(在两者之间执行write(2)),而文件指针位于另一个位置,则会发生许多竞争场景。

Unix(包括所有 posix 系统 linux、bsd 等)保证非阻塞系统调用(如 stat(2) 是)本质上是原子的,在进程(或线程)执行时阻塞文件的 inode系统调用。因此,当您的stat(2) 系统调用正在获取数据时,没有其他线程可以使用该文件。即使在阻塞调用中,unix 也保证对同一个描述符进行的不同系统调用将被链式执行,并且进程/线程必须等待stat(2) 系统调用结束。

fstat(2) 的问题是它必须解决所有路径元素,直到它到达文件的最终 inode(这是存储文件长度的地方),并且这是一个一个完成的基础。直到它没有到达最终的 inode 之前,不会对最终的 inode 进行任何锁定(实际上,在我们到达它之前它是未知的,所以在我们完成 namei() 解析之前我们不能阻止它)然后它解决为原stat(2)

结论

stat(2)其他线程 文件描述符一起使用,而不用担心数据损坏,这是不可能发生的。不要犹豫,因为在您收集 stat 信息时,文件的 inode 不会发生任何事情。

【讨论】:

以上是关于在不知道文件描述符的情况下检测文件大小不为零的最快方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

检查字节数组是不是全为零的最快方法

过滤分组列值之和不为零的数据表

SQL Sub-Select,仅显示不为零的项目

检测到约束模糊地表明高度为零的情况

主机部分不为零的子网

MacOS:NSPredicate 查找“一对一”关系不为零的位置