2012-01-14 54 views
0
struct A { int i; }; 
... 
A *p = (A*) (8); // or A *p = 0; 
p->i = 5; // Undefined Behavior according C/C++ standard 

然而,实际上大部分的系统会崩溃(分段错误)这样的代码。指针间接支票无效的内存访问和分割故障

这是否意味着所有这样的架构/系统都对隐式指针间接检查(即p->)验证它是否访问错误的内存位置?

如果是的话,那么就意味着,即使在完美工作代码我们支付的价格为额外的检查,是否正确?

回答

1

不,不正确。这些完全相同的检查对于有效内存访问是绝对需要的,原因有两个:

1)否则,系统将如何知道您访问的物理内存以及该页是否已经驻留?

2)否则,如果物理内存变得紧张,操作系统如何知道物理内存的哪些页面被分页?

它集成到整个虚拟内存系统中,是现代计算机执行得如此惊人的一部分以及。这不是任何一种单独的检查,它是决定操作访问的物理内存页面的一部分。这是写复制工作的一部分。 (完全相同的检查检测何时需要复制。)

1

分段错误是尝试访问CPU无法物理寻址的内存。它在硬件通知操作系统关于内存访问冲突时发生。所以我认为没有额外的检查,如果尝试访问内存位置失败,则硬件会通知操作系统,然后向操作系统发送一个信号给导致异常的进程。默认情况下,接收信号的进程转储核心并终止。

2

通常没有额外的隐藏检查,这只是使用虚拟内存的效果。

一些潜在的虚拟地址只是没有映射到物理内存,所以翻译像8这样的东西可能会失败。

2

是的,你正在支付额外支票的价格。这不仅仅是指针间接寻址,还有任何存储器访问(除了DMA之外)。但是,支票的成本非常小。

  • 当您的进程正在运行时,页表不会经常更改。页表的一部分将被缓存在翻译后备缓冲区中,访问缓冲区中的条目的页面不会产生额外的损失。

  • 如果您的进程访问没有TLB条目的页面,则CPU必须额外访问内存才能获取该页面的页表条目。它会被缓存。

通过编写测试程序,您可以看到这种效果。给你的测试程序一大块内存,并开始随机读写内存中的位置。使用命令行参数更改大小。

  • 在L1缓存大小以上,由于L2缓存延迟导致性能下降。
  • 在L2高速缓存大小之上,性能将下降到RAM等待时间。
  • 在TLB寻址的内存大小之上,由于TLB未命中,性能会下降。 (这可能会在用完二级高速缓存空间之前或之后发生,具体取决于多种因素。)
  • 高于可用内存的大小,性能将因交换而下降。
  • 在可用交换空间和RAM的大小之上,应用程序将被操作系统终止。

如果您的操作系统允许“大页面”,TLB可能确实能够覆盖非常大的地址空间。也许你可以通过从mmap分配4k块来破坏操作系统,在这种情况下,根据你的处理器,TLB未命中可能只有几兆工作集。

但是:小的性能下降必须权衡虚拟内存的好处,这些虚拟内存的好处不胜枚举。

+0

看起来很奇怪,你的答案显然是“不”,但你从“是”开始。您不需要额外的有效性检查,它需要实现虚拟内存。 – 2012-01-14 06:21:20

+0

@DavidSchwartz:我认为页面权限检查是虚拟内存的一部分,所以是的,由于虚拟内存有成本,所以检查费用很高。你不必同意这一点,但你不必争论语义。 – 2012-01-14 06:39:44

+0

他问工作代码是否支付导致无效代码出错的*额外*有效性检查的费用。它不是。额外的有效性检查没有成本,因为没有额外的有效性检查。有效性检查不是作为额外的检查来实现的,而是为了使有效的代码有效而需要进行的部分检查。这不仅仅是一个语义问题,一个没有仔细阅读你的答案并批判性地结束与真相相反的印象的人。 – 2012-01-14 06:41:42

1

首先,你需要阅读和理解:http://en.wikipedia.org/wiki/Virtual_memory#Page_tables

那么通常的情况是,当一个进程尝试取消引用无效的虚拟内存的位置,操作系统捕获由MMU提出的缺页异常(请参阅上面的链接)了解无效虚拟地址(0x0,0x8,无论)。操作系统然后在其页表中查找地址,找不到它,并向导致进程崩溃的进程发出SIGSEGV信号(或类似信号)。

有效地址和无效地址之间的区别在于操作系统是否为该地址范围分配了页面。大多数操作系统都设计为从不分配第一个页面(从0x0开始),这样NULL解除引用总是会崩溃。

因此,你所称的“额外检查”是否与每个单页错误,有效地址发生的检查完全相同 - 这只是页表查找是否成功的问题。