1

这是我在阅读<Windows via C/C++ 5th Edition> 时遇到的另一个问题。首先,让我们看一些引文。

LPVOID WINAPI VirtualAlloc(
  __in_opt  LPVOID lpAddress,
  __in      SIZE_T dwSize,
  __in      DWORD fdwAllocationType,
  __in      DWORD fdwProtect
);

最后一个参数 fdwProtect 表示应该分配给区域的保护属性。 与区域关联的保护属性对映射到该区域的提交存储没有影响。

保留区域时,分配将最常用于该区域的存储的保护属性。例如,如果您打算提交具有 PAGE_READWRITE 保护属性的物理存储,则应使用 PAGE_READWRITE 保留该区域。当区域的保护属性与提交存储的保护属性匹配时,系统的内部记录保存会更有效。

(提交存储时)...您通常传递调用 VirtualAlloc 以保留区域时使用的相同页面保护属性,尽管您可以指定不同的保护属性。

上面的引用完全让我感到困惑。

  • 如果与区域关联的保护属性对提交的存储没有影响,我们为什么需要它?

  • 既然建议对保留和提交使用相同的保护属性,为什么 Windows 仍然为我们提供使用不同属性的选项?这不是误导和悖论吗?

  • 保留区域和已提交存储的保护属性分别存储在哪里?

非常感谢您的见解。

4

2 回答 2

3

在上下文中阅读它很重要。

与区域关联的保护属性对映射到该区域的提交存储没有影响。

指的是保留,而不是承诺区域。

保留页面没有后备存储,因此无论您传递给 VirtualAlloc 什么,它的保护在概念上始终是 PAGE_NOACCESS 。即,如果线程试图读/写保留区域中的地址,则会引发访问冲突。

来自链接的文章:

保留地址始终为 PAGE_NOACCESS,无论将什么值传递给函数,系统都会强制执行该默认值。提交的页面可以是只读的、读写的或不可访问的。

回覆:

  • 保留区域和提交存储的保护属性分别存储在哪里?

每个进程的虚拟地址区域的保护属性存储在 VAD 树中。(VAD == 虚拟地址描述符,请参阅Windows Internals或链接文章)

既然建议对保留和提交使用相同的保护属性,为什么 Windows 仍然为我们提供使用不同属性的选项?这不是误导和悖论吗?

因为该函数总是接受一个保护参数,但它的行为取决于fdwAllocationType. 保护只对提交的存储有意义。

Richter 建议使用相同保护设置的原因可能是因为区域中保护标志的更改越少意味着“块”越少(请参阅您的书以了解定义),因此 VAD 的 AVL 树更小。即,如果一个区域中的所有页面都使用相同的标志提交,那么将只有 1 个块。否则,该区域中的块数可能与页数一样多。您需要为每个块(而不是页面)提供一个 VAD。

块 == 具有相同保护/状态的连续页面集。

如果与区域关联的保护属性对提交的存储没有影响,我们为什么需要它?

如上。

于 2010-11-23T10:04:41.137 回答
2

嗯...一个原因可能是你可以使用保护页,这样你就可以在使用它时提交内存。

想想 Windows 中的线程堆栈;堆栈下面的页面被设置为保护页面,通常具有读写能力。一旦接触到保护页面,异常处理程序就会运行并提交保护页面并使下一页成为保护页面。

请参阅此处以获得更好的描述。此外,该链接是关于 windows 如何处理低级资源的系列的一部分,并且读起来非常好。

允许您重新指定保护属性的另一个原因可能是写时复制技术。页面设置为只读,直到它们被更改,这可能会引发您可以处理的异常等等等等。

在英特尔 386 系列芯片上,提交、读/写/保留标志存储在页表中。查看386 芯片参考以了解更多详细信息。编辑:我看了一会儿,找不到 MS 存储 PAGE_GUARD 位的位置。现在我很好奇我在哪里看到的。:) 太糟糕了,去年春天我扔掉了大约 500 磅的旧参考资料……

希望这可以帮助 :)

于 2010-11-23T08:49:03.623 回答