【问题标题】:Accessing unallocated page访问未分配的页面
【发布时间】:2016-04-22 01:56:06
【问题描述】:

在虚拟内存的概念中,物理页框只有在对应的页在虚拟空间中被使用时才被分配。

我想知道这种分配何时发生。

我试图引用一些随机选择的地址,但大多数时候,它给了我一个分段错误。我猜大多数页面都被标记为未使用,仅仅读取页面不足以强制操作系统为我分配一个物理页框。 (我用 GDB 试过这个)

【问题讨论】:

  • 我想知道这种分配何时发生。在操作系统调用中。
  • 您只能引用程序分配的内存,无论是作为静态或自动存储的一部分,还是作为动态分配函数调用的结果。

标签: c++ operating-system gdb virtual-memory


【解决方案1】:

您描述的访问冲突是逻辑页面无效的结果;不是他们没有被访问的事实。在虚拟内存中创建一个真实页面需要几个分配步骤。

要理解这一点,需要拆分逻辑内存翻译和虚拟内存的概念。

一个内存管理单元提供一个连续的逻辑地址空间。在该地址空间内,页面可能会或可能不会映射到物理页框。虽然地址空间可能是连续的,但有效页面的范围通常是不连续的。

内存管理单元使用 PAGE TABLE 将逻辑地址转换为物理地址。

处理器通常使用多个页表或嵌套页表(一个表引用另一个表引用另一个以标识页框)。在前一种情况下,页表的长度可能比整个地址范围短。后者也可能如此,但此外,页表引用嵌套表(可能有空条目)。

这个页表结构是用进程创建的。拥有页表 ENTRY 是拥有映射的先决条件。在具有嵌套页表的系统上,可以通过添加条目来调整页表的大小。表的大小通常受系统参数或进程配额的限制。 (忽略 Unix 克隆、具有持久性 shell 的系统和系统表)在进程启动时,页表条目不涉及任何内容。

程序加载器对页面映射进行初始设置。这将设置页表,以便程序(由 LINKER 定义)在页表中具有有效地址。

在大多数情况下,实际的页框在被访问之前不会被映射到该页。但是,您不会在您的应用程序中看到这一点。如果页表表明该页是有效的,并且您引用了一个没有物理框架的页,则会触发页错误。然后操作系统将创建页框映射并重新启动您的应用程序。 (虚拟内存)

您的应用程序可以在运行时映射其他页面。 New 和 Malloc 在幕后为您执行此操作,但您也可以直接执行此操作。当您创建此类映射时,您正在更改页表,以便它说逻辑页是有效的。然后,您通常必须引用该页面以供操作系统将逻辑页面映射到物理页面框架。

(假设您没有尝试写入或执行)简而言之,您描述的是由于没有有效的页表条目而导致逻辑内存转换失败 对于您尝试访问的页面。

【讨论】:

    【解决方案2】:

    操作系统会处理这个问题。要查看它的实际效果,您需要对 OS 内核代码进行检测或应用调试器,但以下人员可能会看到这个概念:

    int *p = new int[1000000];
    

    这将分配大约 4MB(1000 页)的内存,但到目前为止没有一个被使用,所以没有一个会被“物理分配”(但也许第一个实际上有,因为它很可能用于存储分配的元数据)

    p[2048] = 42; 
    

    现在,操作系统将在分配中处理 8192 字节的页面错误,一旦完成,就可以将值 42 写入该页面。

    运行 GDB 不会显示这个。除了它比写入已经“提交”的物理页面要慢得多这一事实之外,它并不明显 - 您可以通过在 100MB 数据中每 4K 写入一个元素或在相同 100MB 中写入前 250000 个条目来尝试这一点 -然后再写第二遍。第二次两者都会更快,但由于第二次没有发生页面错误,第一次情况下的第二次会明显更快。

    一个例子:

    #include <iostream>
    #include <chrono>
    #include <functional>
    #include <memory>
    
    void measure(const std::string& test, std::function<void()> function)
    {
        auto start_time = std::chrono::high_resolution_clock::now();
    
        function();
    
        auto duration = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now() - start_time);
        std::cout<<test<<" "<<static_cast<double>(duration.count()) * 0.000001<<" ms"<<std::endl;
    }
    
    const int NWRITES = 1024*1024;
    const int PAGESTEP = 1024;
    const int NINTS = NWRITES*PAGESTEP; /* 1024M * sizeof(int) = 4GB */
    
    int main()
    {
        std::unique_ptr<int[]> p(new int [NINTS]);
    
        measure("Every int", [&p](){ for(int i = 0; i < NWRITES; i++) p[i] = i; });
        measure("Every 4KB", [&p](){ for(int i = 0; i < NWRITES; i++) p[i*PAGESTEP] = i; });    
        measure("Every int", [&p](){ for(int i = 0; i < NWRITES; i++) p[i] = i; });
    measure("Every 4KB", [&p](){ for(int i = 0; i < NWRITES; i++) p[i*PAGESTEP] = i; });
    }
    

    给出这样的东西:

    Every int 10.3651 ms
    Every 4KB 1856.2 ms
    Every int 2.4179 ms
    Every 4KB 84.1603 ms
    

    【讨论】:

      猜你喜欢
      • 2014-02-16
      • 2015-03-08
      • 1970-01-01
      • 1970-01-01
      • 2019-06-18
      • 1970-01-01
      • 2014-05-24
      • 2012-08-30
      • 1970-01-01
      相关资源
      最近更新 更多