Mouri_Naruto 发表于 2019-5-26 22:59:29

【Windows】一种快速文件遍历方法的实现思路

本帖最后由 Mouri_Naruto 于 2019-5-26 23:02 编辑

附:对站长表示深深的感谢,给了我在这里发帖的机会。

先贴代码,毕竟一些人看帖的目的就是为了看代码,而且本文的重点并不是那些代码,而是我这样写的原因。
https://github.com/M2Team/NSudo/commit/0b4fc388baa54637e6a016bc5809ee6fbd946009
当然,由于自己当时比较仓促还没有对函数和结构体的定义添加参数,表示抱歉。

这个方案说白了就是使用 FileIdBothDirectoryRestartInfo 和 FileIdBothDirectoryInfo 这两个 FileInformationClass 调用 GetFileInformationByHandleEx API 来实现文件的定义。
原理并不复杂,而且我写的代码风格偏纯 C(虽然一些地方用的是 C++ 的特性),相信你们很快也能看懂。

于是,这篇文章主要是记录我那样设计的原因:

0、为什么使用偏纯 C 的语法?
答:有三个理由。一是偏纯 C 语法看起来比较清晰。二是不希望生成一些诸如栈回退处理程序、C++ 异常等类似的机器实现。三是希望做到 ABI 兼容以更方便地与自己开发的其他工具共享实现。

1、为什么不学习 Everything 遍历 USN?
答:USN 是 NTFS 文件系统的独有特性,并不是一种通用的解决方案。

2、为什么选择 GetFileInformationByHandleEx API?
答:虽然 GetFileInformationByHandleEx 内部关于 FileIdBothDirectoryRestartInfo 和 FileIdBothDirectoryInfo 这两个 FileInformationClass 的实现是直接调用 NtQueryDirectoryFile 实现的。但 NtQueryDirectoryFile 并不是完全文档化的 API,为了自己的其他项目更方便使用自己的实现,于是自从两年前我就尽量使用文档化 API。

3、为什么 FileInfoBuffer 的大小设置为 32768 字节?
答:其实,其实微软的文档并没有说明我们应该设置多大效率才能达到最高。.Net Core 的文件遍历类实现直接使用的 NtQueryDirectoryFile 缓冲区设置的是 4096 字节。我选择 32768 字节,主要是因为我曾经以遍历 C:\Windows\WinSxS\Manifests 目录做过一次实验。调用 GetFileInformationByHandleEx 的次数随着缓冲区大小的增加而减少,但是缓冲区大小超过 32768 字节后,调用 GetFileInformationByHandleEx 的次数就不会减少了,而且因为缓冲区变大,分配内存块时间增加,反而拖慢速度。

4、为什么不直接返回 FILE_ID_BOTH_DIR_INFO 结构体,而是返回自制的 M2_FILE_ENUMERATOR_INFORMATION 结构体?
答:我宁愿多一次内存拷贝,也要这么做,主要有两点原因。一是 FILE_ID_BOTH_DIR_INFO 结构体的 ShortName 和 FileName 这两个成员对应的字符串是没有 '\0' 终止标志,导致使用起来很难受。二是 FILE_ID_BOTH_DIR_INFO 结构体的一些成员显示出来要么失去了自己单独封装意义,要么用不到,要么用起来不够友好。

5、这样实现性能大概能提升多少?
答:完整遍历 4000 次 C:\Windows\WinSxS\Manifests 目录,相对系统的默认方式,也就是 FindFirstFile 和 FindNextFile 组合使用,大概有 30% 左右的速度提升。(从 151972ms 缩减到 112366ms)

6、为什么传入的是句柄而不是文件名?
答:作为 Dism++ 的核心开发者之一的我,势必会碰到要求进行大量文件操作的情况。譬如系统垃圾清理,就删除一个文件来说,至少要进行三种操作(获取文件的大小、去除文件的只读属性和删除文件)。如果直接使用系统提供的 API,其内部实现是这样的:
打开文件句柄 -> 获取文件大小 -> 关闭文件句柄 -> 打开文件句柄 -> 去除只读属性 -> 关闭文件句柄 -> 打开文件句柄 -> 添加删除标记 -> 关闭文件句柄
为了减少反复打开和关闭文件句柄的次数,做到以下的效果;而且也为了规范操作步骤,所以我搞了一套通过句柄操作文件的实现。
打开文件句柄 -> 获取文件大小 -> 去除只读属性 -> 添加删除标记 -> 关闭文件句柄
事实上,这样做后,在文件操作方面会有比较大的效率提升,至少能减少系统调用引起模式切换的次数。

很久没写技术文章了,手有点生疏了,大家见谅。

毛利

xtfpg 发表于 2019-8-13 17:55:40

专业。。。。。。。。。。。。。。。。。。。
页: [1]
查看完整版本: 【Windows】一种快速文件遍历方法的实现思路