最后一个映射页面

时间:2011-07-14 09:16:56

标签: mmap memory-mapped-files

POSIX说“系统总是零填充对象末尾的任何部分页面。此外,系统永远不会写出超出其结尾的对象最后一页的任何修改部分。”< / em>,Linux和FreeBSD文档在他们的手册页中都有类似的措辞 这表明尽管读取最后的尾随字节(因为它们在映射范围之外)并不是严格合法的,但它仍然是定义良好的并且以某种方式设计,因此可能发生而不会崩溃。甚至写到那个区域也是有条理的。

另一方面,Windows文档没有说明在小于块大小的范围内跟踪字节的事情,并且确实警告说创建大于文件的映射会增加文件大小而不一定< / em>将数据归零 我倾向于认为这是错误的信息还是历史性的(可能追溯到Win95?)。 SetFileValidData需要非标准用户权限,因为安全问题可能会使先前删除的文件中的数据可见。如果Windows内核开发人员允许任何人通过映射任何随机文件来轻易绕过这一点,那么他们就必须非常愚蠢。
我在Windows XP上的观察是,任何新页面显然是从零池中提取的,对于空页面回写,要么文件是静默稀疏的,要么以非常非常智能的方式完成回写(任何时候都没有明显的延迟)时间,即使在千兆字节范围内。)

那么问题是什么?

我需要计算(可能是数千个)文件的哈希值,以检测已修改的文件子集。可以假设SHA-256为算法,但实际算法并不重要 这当然不是什么大挑战,但是像每个软件一样,它应该立即运行并且不使用内存,依此类推。通常的现实期望,你得到它: - )

计算这种散列的常规方法是检查消息是否具有根据散列函数的块大小(例如,例如64字节)的大小,并且如果不是这种情况,则对最后一个不完整的块进行零填充。此外,哈希可能具有对齐要求 这通常意味着您必须制作完整的消息副本,或者编写一些特殊代码,这些代码除了一个块之外还有一个零填充副本以及最后一个块的零填充副本。或类似的东西。哈希算法经常默默地代表自己做这种事情。无论如何,它涉及到移动大量数据和比预期更复杂的数据。

现在有一种诱惑就是直接散列内存映射文件,并依赖于文件映射必然依赖于内存页面这一事实。因此,起始地址和物理映射长度都或多或少地保证为4kB的倍数(在某些系统上为64kB)。这当然意味着它们自动也是64,128或者散列可能具有的任何其他块大小的倍数 出于安全原因,实际上没有操作系统能够为您提供包含陈旧数据的页面。

这意味着您可以天真地对整个文件进行哈希处理,而无需担心对齐,填充或任何内容,并避免复制数据。 可能读取映射范围结束后的几个字节,但它仍然必须在同一页面内。

我当然知道这技术上非法。读取映射范围之外的最后一个字节有点类似于说malloc(5)总是返回一个8字节的块,所以使用额外的3个字节是安全的。

虽然,除了那个显而易见的事情之外,我的假设是“这只会”合理,或者是否存在一些我在任何主要平台上都看不到的严重问题?

我对理论或历史操作系统并不是太感兴趣,但我想保持有点可移植。也就是说,我希望确保它可以在桌面计算机或“典型托管服务器”上遇到的任何问题上可靠地运行(因此,主要是Windows,Linux,BSD,OSX)。 如果存在一个1985年的操作系统,它标记了最后一页不可读并在其错误处理程序中强制执行严格的字节范围,那我就可以了。你不能(也不应该)让每个人都开心。

1 个答案:

答案 0 :(得分:1)

  

计算此类哈希的常规方法是检查消息是否具有与散列函数的块大小(例如,例如64字节)相对应的大小,并且如果不是这种情况,则对最后一个不完整的块进行零填充。 / p>

不是真的。这样你就无法找到最后一个块的长度(那里是零还是来自填充)。填充的工作方式略有不同:在一个模式中,您总是会添加一个1,然后0,直到块结束。

如果数据以块边界结束,则表示需要另一个块。这个额外的块可能属于额外的页面。所以我认为它不会像你描述的那样起作用。

  

它可能会在映射范围的末尾读取几个字节,但它仍然必须位于同一页面内。

我认为这应该适用于英特尔/ AMD,因为没有任何人可以对付它。 i386 + CPU具有分段和页面。段可以在任何字节边界结束,但AFAIK没有当前操作系统使用它们。所以,只要你留在你的页面,它就是你的全部。

所以我认为它可以像这样工作:

  • 如果最后一个块没有完整的大小,请执行填充
  • 否则,在准备好的常量块上运行最后一轮,例如1000000000000000