struct A { int i; };
...
A *p = (A*) (8); // or A *p = 0;
p->i = 5; // Undefined Behavior according C/C++ standard
但是,对于此类代码,实际上大多数系统都会崩溃(分段错误)。
这是否意味着所有此类架构/系统都对指针间接(即p->
)进行了隐藏检查,以验证它是否正在访问错误的内存位置?
如果是,那么它意味着即使在完美工作代码中我们为额外支票付出代价,对吗?
答案 0 :(得分:2)
通常没有额外的隐藏检查,这只是使用虚拟内存的效果。
某些潜在的虚拟地址并未映射到物理内存,因此翻译8这样的内容可能会失败。
答案 1 :(得分:2)
是的,您正在支付额外支票的价格。它不仅适用于指针间接,而且适用于任何内存访问(除了DMA之外)。但是,支票的费用非常小。
当您的进程正在运行时,页表不会经常更改。页面表的某些部分将缓存在转换后备缓冲区中,访问缓冲区中包含条目的页面不会产生额外的惩罚。
如果您的进程访问没有TLB条目的页面,则CPU必须进行额外的内存访问才能获取该页面的页表条目。它将被缓存。
您可以通过编写测试程序来了解此操作的效果。为测试程序提供大量内存,并开始随机读取和写入内存中的位置。使用命令行参数更改大小。
如果您的操作系统允许"大页面",TLB可能确实能够覆盖非常大的地址空间。也许你可以通过从mmap
分配4k块来破坏操作系统,在这种情况下,TLB未命中可能只有几兆工作集,这取决于你的处理器。
但是:必须权衡小的性能下降与虚拟内存的好处,虚拟内存太多,无法在此列出。
答案 2 :(得分:1)
不,不正确。有效的内存访问绝对需要完全相同的检查,原因有两个:
1)否则,系统将如何知道您正在访问的物理内存以及该页面是否已驻留?
2)否则,如果物理内存变紧,操作系统如何知道哪些页面的物理内存会被分页?
它集成到整个虚拟内存系统中,是现代计算机如此惊人地执行井的部分原因。它不是任何单独的检查,它是确定操作访问哪个物理内存页面的过程的一部分。这是写拷贝工作的一部分。 (同样的检查会检测何时需要复制。)
答案 3 :(得分:1)
分段错误是尝试访问CPU无法物理寻址的内存。当硬件通知操作系统有关内存访问冲突时,会发生此错误。因此,我认为没有额外的检查,如果尝试访问内存位置失败,硬件会通知操作系统,然后操作系统会向导致异常的进程发送信号。默认情况下,接收信号的进程转储核心并终止。
答案 4 :(得分:1)
首先,您需要阅读并理解这一点:http://en.wikipedia.org/wiki/Virtual_memory#Page_tables
因此,通常发生的情况是,当进程尝试取消引用无效的虚拟内存位置时,操作系统会捕获MMU引发的页面错误异常(请参阅上面的链接)以获取无效的虚拟地址(0x0,0x8,无论如何)。操作系统然后在其页表中查找地址,找不到它,并向进程发出SIGSEGV信号(或类似信号),导致进程崩溃。
有效地址和无效地址之间的区别在于操作系统是否为该地址范围分配了一个页面。大多数操作系统设计为永远不会分配第一页(从0x0开始的那一页),因此NULL解引用将始终崩溃。
所以你所谓的“额外检查”实际上是针对每个单页错误,有效地址发生的相同检查 - 这只是页表查找是否成功的问题。