有时错误会导致内存访问冲突异常。
如何触发此异常?幕后工作机制是什么?
它是否需要来自CPU的支持(从什么CPU开始?)/来自OS(从哪个版本开始?)/来自编译器(从什么版本开始?)
修改
我想要更好地理解的一个特定场景:
以下代码可能会导致访问冲突异常。
TCHAR* czXXX= _T("ABCDEFG");
czXXX[0]= 'A';
我猜czXXX指向一个只读内存块,但到底发生了什么?
答案 0 :(得分:11)
内存访问违规是一个很大的主题:)
The Protection of Information in Computer Systems(自1973年起):)摆脱 segments 的机制,其中进程被分配了一个 base 和一个绑定;任何尝试访问base:base+bound
范围之外的内存意味着程序已经做了些傻事,应该被杀死。
80x86系列处理器实现基本的段支持,GEMSOS安全内核是基于此机制的A1认证操作系统内核。
但是细分市场并不是非常动态,几乎所有现代操作系统都是paging系统,当页面内存不可用时。这依赖于具有MMU内存管理单元的CPU,该单元检查所有内存访问以获得正确的权限以及是否存在正确的内存映射。当进程尝试访问当前未映射到RAM的内存时,MMU会向CPU发出已发生故障的信号,并且CPU暂停进程以从磁盘加载请求的内存页。 (或者,如果不应该为进程映射内存,假设它尝试访问0x0
或某些未使用mmap
或类似内存分配原语映射的随机内存位置,则会导致过程。)
英特尔80386是第一款支持分页的英特尔芯片,这就是为什么Windows 3.1的“386增强模式”所以比286模式好得多。
编译器并不真正参与,但CPU,MMU和操作系统内核必须一起工作。
答案 1 :(得分:6)
在x86架构(以及其他大多数架构)中,这是从MMU(内存管理单元)开始的。 MMU用于将虚拟内存地址转换为物理内存地址。如果请求访问无效地址(0x00000000或太高),MMU将陷阱(中断)到操作系统(实际上这是针对不在TLB中的每次访问完成的(Translate Lookaside Buffer - MMU转换“缓存) “))。在这里操作系统将能够告诉这是非法的内存访问,并通过依赖于操作系统的机制传播到用户应用程序(Linux(SIGSEGV)中的信号,我不熟悉Windows足以说明它是如何完成的)在它)。
此功能适用于任何现代CPU,操作系统和编译器。最基本的要求是MMU,它存在于除最基本的嵌入式CPU之外的所有CPU中。我怀疑目前有任何不支持此操作的PC。
修改强>
在OP编辑之后,当使用文字字符串时,内存将放在可执行文件的.text段中。这是二进制代码和常量值所在的位置。当然,在大多数操作系统中,这是只读的(特别是在具有各种安全增强功能的Linux下)。当您尝试更改文字字符串的值时,您基本上是在尝试写入只读内存,从而导致访问冲突。同样,这被MMU捕获,该MMU看到对只读存储器地址的写命令。
答案 2 :(得分:2)
当您尝试访问内存地址时,计算机会执行以下几个步骤:
如果地址不在内存中,CPU将生成内存检查异常。此时,操作系统接管了。
如果此时内存不可用,则有两种可能性之一。地址不可用,或者您没有所需的权限(例如,尝试写入只读内存)。在这种情况下,操作系统会将访问冲突传递给进程。
对于CPU和OS版本,这是允许虚拟内存的任何系统。我不知道这个细节。
答案 3 :(得分:2)
所有这些答案都解释了处理器级别的情况。如上所述,一旦处理器向操作系统发出中断,就会随着不同的操作系统而改变。
Windows使用称为“Structured exceptions”的机制。非常重要的是不要将它与C ++异常混淆,它们是不同的。结构化异常概念性的工作方式与C ++异常相同,因为它们可以展开堆栈以寻找处理程序。由于结构化异常与语言无关,因此它们不调用析构函数或进行任何清理。
可以使用
捕获结构化异常__try
{
//Usual code
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
//Handling code
}
__finally
{
//Cleanup
}
但请注意,一旦您收到结构化异常,您的程序已经崩溃,因此没有理智的“恢复”。
有关SEH的更多信息。
答案 4 :(得分:1)
此处也可能发生内存访问冲突:
delete pSample;
//again deleting the same memory!
delete pSample;
对于这种情况,从OS的内存模型中引发异常。主要是操作系统的工作是验证任何进程的内存访问权限!
答案 5 :(得分:0)
void Kaboom()
{
int* certain_death = NULL;
*certain_death = 0;
}