“EXC_BAD_ACCESS”与“分段错误”。 实际上是否相同?
在我的前几个虚拟应用程序中(为了练习而学习)我遇到了很多EXC_BAD_ACCESS
,它以某种方式告诉我Bad-Access是:您正在触摸/访问您不应该访问的对象,因为它尚未分配或取消分配或只是您无权访问它。
看看这个具有错误访问问题的示例代码,因为我试图修改一个const
:
-(void)myStartMethod{ NSString *str = @"testing"; const char *charStr = [str UTF8String]; charStr[4] = '\0'; // bad access on this line. NSLog(@"%s",charStr); }
虽然分段错误说 :分段错误是一种特殊的错误,它是由访问“不属于你”的内存引起的。它是一种帮助机制,可以防止破坏内存并引入难以调试的内存错误。 每当你遇到段错误时,你就知道你在做错了记忆( 这里有更多描述)。
我想知道两件事。 一 ,我对objective-C's EXC_BAD_ACCESS
吗? 我做对了吗?
第二 , EXC_BAD_ACCESS and Segmentation fault
是否有EXC_BAD_ACCESS and Segmentation fault
,Apple刚刚即兴创作了它的名字?
不, EXC_BAD_ACCESS
与SIGSEGV
。
EXC_BAD_ACCESS
是Machexception(Mach和xnu的组合构成Mac OS X内核),而SIGSEGV
是POSIX信号。 当发生崩溃且原因为EXC_BAD_ACCESS
,通常在以下情况后立即在括号中报告信号:例如, EXC_BAD_ACCESS(SIGSEGV)
。 但是,还有一个POSIX信号可以与EXC_BAD_ACCESS
一起看到:它是SIGBUS
,报告为EXC_BAD_ACCESS(SIGBUS)
。
SIGSEGV
最常见于读取/写入内存映射中根本没有映射的地址(如NULL
指针)或尝试写入只读存储器位置(如上例所示)。 另一方面,即使对于进程具有合法访问权限的地址,也可以看到SIGBUS
。 例如, SIGBUS
可以使用假定对齐地址的指令或者尝试写入其没有特权级别的页面的进程来打击一个敢于从未对齐的内存地址加载/存储的进程。 。
因此, EXC_BAD_ACCESS
可以最好地理解为SIGSEGV
和SIGBUS
的集合,并且指的是错误访问内存的所有方式(无论是因为所述内存不存在,还是存在但是未对齐,特权或诸如此类),因此其名称: exception – 访问不好 。
为了大饱眼福,这里是代码,在xnu-1504.15.3 (Mac OS X 10.6.8 build 10K459)
内核源代码中,文件bsd/uxkern/ux_exception.c
从第429
行开始,将EXC_BAD_ACCESS
转换为SIGSEGV
或者SIGBUS
。
/* * ux_exception translates a mach exception, code and subcode to * a signal and u.u_code. Calls machine_exception (machine dependent) * to attempt translation first. */ static void ux_exception( int exception, mach_exception_code_t code, mach_exception_subcode_t subcode, int *ux_signal, mach_exception_code_t *ux_code) { /* * Try machine-dependent translation first. */ if (machine_exception(exception, code, subcode, ux_signal, ux_code)) return; switch(exception) { case EXC_BAD_ACCESS: if (code == KERN_INVALID_ADDRESS) *ux_signal = SIGSEGV; else *ux_signal = SIGBUS; break; case EXC_BAD_INSTRUCTION: *ux_signal = SIGILL; break; ...
根据您的另一个问题进行编辑
请注意,此处的exception并未引用语言级别的exception,类型1可能会遇到类似try{} catch{}
块的语法糖。 这里的exception是指CPU在遇到程序中某些类型的错误时的操作(它们可能是也可能不是致命的),如空指针解除引用,需要外部干预。
当发生这种情况时,CPU会提升通常称为exception或中断的内容 。 这意味着CPU保存它正在做的事情( 上下文 )并处理exception情况。
为了处理这种exception情况,CPU不会在应用程序中开始执行任何 “exception处理”代码( catch
-blocks等)。 它首先通过开始执行内核提供的一段称为中断服务例程的代码来提供OS控制。 这是一段代码,用于确定哪个流程发生了什么,以及该怎么做。 因此,操作系统有机会判断情况,并采取它想要的行动。
它对无效内存访问所做的操作(例如空指针解除引用)是用EXC_BAD_ACCESS(SIGSEGV)
发出有罪进程的信号。 它为未对齐的内存访问所做的操作是用EXC_BAD_ACCESS(SIGBUS)
向有罪进程发出信号。 还有许多其他例外情况和相应的行动,并非所有情况都涉及信号。
我们现在回到您的计划的背景下。 如果您的程序收到SIGSEGV
或SIGBUS
信号,它将调用为该信号安装的信号处理程序 ,如果没有,则调用默认值。 人们很少为SIGSEGV
和SIGBUS
安装自定义处理程序,默认处理程序关闭程序,因此您通常会关闭程序。
因此,这种exception完全不同于try{}
-blocks和catch{}
es中的一次throw
。 这些exception仅在应用程序内处理,而根本不涉及操作系统。 这里发生的事情是, throw
语句只是对处理该exception的最内部catch
块的美化跳转。 当exception通过堆栈冒泡时,它会展开它后面的堆栈,根据需要运行析构函数等。
基本上是的,确实EXC_BAD_ACCESS通常与SIGSEGV配对, SIGSEGV是一个警告分段失败的信号。
每当您使用指向无效数据的指针(可能不属于该进程,可能是只读的,通常可能是无效地址)时,分段失败就会增加。
不要在“访问对象”方面考虑分段错误,而是访问内存位置,所以是地址。 OS存储器保护系统必须认为该地址是连贯的。
并非所有与访问无效数据相关的错误都可以由内存管理器跟踪,考虑指向堆栈分配变量的指针,尽管在恢复堆栈帧时其内容无效,但这被认为是有效的。