【问题标题】:Question about the NULL-Pointer Assignment Partition in a process' address space关于进程地址空间中的NULL-Pointer Assignment Partition问题
【发布时间】:2010-11-22 05:54:32
【问题描述】:

我正在阅读 以下是一些引用:

每个进程的虚拟地址空间是 分成分区。在 x86 32 位 Windows,0x00000000 的分区 - 0x0000FFFF(含)被调用 NULL 指针分配分区。 这个分区是为了帮助 程序员捕获 NULL 指针 作业。如果你的线程 一个进程尝试读取或 写入此内存地址 分区,访问冲突是 提出来。

我想知道,为什么我们必须使用一个范围的地址空间而不是只是值0 捕捉 NULL 指针赋值? AFAIK,NULL 等于 0。那么这个设计背后的考虑是什么?在这个范围内还有什么用户不应该接触的东西吗?还是NULL不一定等于0?

非常感谢。

【问题讨论】:

    标签: windows memory process


    【解决方案1】:

    所有现代操作系统都使用虚拟内存管理硬件巧妙地实现了空指针取消引用检查,而没有执行开销。第 0 个 4 KB 页面(或在本例中为第 0-15 个页面(总计 64 KB))已设置好,因此任何数据读取或写入或在地址 0(或 0x00000FFF 或 0x0000FFFF)处执行的任何数据都会立即导致访问违规异常。

    让我换一种说法。将空指针取消引用捕获为 0,但允许对(例如)地址 1 的数据引用会非常昂贵——因为您无法使用页面粒度 VM 硬件,因此需要执行空指针比较和分支序列在许多指针取消引用之前。通过使第一页或前几页不可访问,可以在 VM 硬件中“免费”完成此检查。缺点是您不能将这些首页用于其他任何用途。

    (理论上,可以使用充分优化的编译器来确定哪些指针取消引用不可能是空指针取消引用,但在出现这种聪明的编译器之前,必须使用未映射的 0 页技巧。)

    那么为什么 Windows(Win98 后)空指针分配分区是 64 KB 而不是最小必需的 4 KB? Wyzard 的回答也为捕获空指针数组索引错误提供了一个很好的案例。

    * 延迟更新:RE:为什么是 64 KB? ——我问了一个应该知道 Windows Core 团队的人,他们说这可能是一个 64 KB 的区域,因为 Windows 将某些内存管理结构保持在 64 KB 的分配粒度。这是为什么?我不知道,但也许 Raymond Chen 有答案:http://blogs.msdn.com/b/oldnewthing/archive/2003/10/03/55239.aspx。哇。 *

    【讨论】:

    • 感谢您的回复。似乎根本原因在于工程细节,而不是纯粹的逻辑。
    【解决方案2】:

    在 C 或 C++ 语言中,NULL 始终等于 0,但在源代码中作为指针值的 0 不一定对应于编译后的二进制文件中的所有零位。但是,在 Windows 上,我相信空指针确实是全零位。

    保留空间可能有助于捕获 myarray[n](其中 myarray 为空)或 mystruct->myfield(其中 mystruct 为空)之类的内容。被访问的地址不一定是指针指向的确切位置,它可能在它之后的某个地方。

    【讨论】:

    • 感谢您的回复。 0xFFFF 的大小为 64K。如果我尝试访问 myarray [64K+1],其中 myarray 为空,这会被捕获吗?我不太明白为什么 0 作为源代码中的指针值不一定对应于编译后的二进制文件中的所有零位,您能否对此进行更多说明?谢谢。
    • @smwikipedia,我想这不会被捕获,虽然我没有尝试过。我不知道地址空间中的 0x10000 是什么。关于空值的表示,想象一个假设的平台,其中“空指针”由内存地址 0xFFFFFFFF 表示,因为 0x00000000 包含一些假设的重要平台特定数据。您仍然会在源代码中写入 0(或 NULL),编译器会将其转换为机器代码中的 0xFFFFFFFF。
    • Wyzard,您可能会很高兴知道在 Microsoft C++ 中的某些情况下,指向数据成员的空指针(一种相当奇怪的指针)实际上表示为 -1 全一位模式.
    【解决方案3】:

    该范围是必要的,以简化由取消引用 null 和接近空指针引起的错误检测。例如:

    int startOffset = 1000; //start at 1000th element;
    char* buffer = obtain();// happens to be null
    for( int i = startOffset; buffer[i] != 0; i++ ) {
       //do stuff
    }
    

    请注意,上述示例中从未取消引用空指针。然而,由于buffer 是一个空指针,因此地址接近于空,使用“分区”技术可以轻松捕获它们的解引用。

    【讨论】:

      【解决方案4】:

      需要一个范围,因为当取消引用指向结构或类元素的指针时,实际访问的内存将是指针,加上结构中成员的偏移量。

      struct Foo {
          int a;
          inb b;
      }* Bar = 0;
      
      Bar->b = 0;
      

      在 int = 32 位编译器上,“b”成员将占结构的 4 个字节,因此 Bar->b 将尝试访问地址 0x00000004。

      【讨论】:

        猜你喜欢
        • 2011-05-26
        • 2011-11-21
        • 1970-01-01
        • 2016-12-05
        • 1970-01-01
        • 2021-10-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多