众所周知,0 地址(标记为宏“NULL”)是非法访问的。我想知道操作系统(比如linux)如何确定何时可以访问代码中某处的空地址,而不必访问代码中的每个指针地址?我认为它与信号有关,特别是与“sigsegv”信号有关。
但我不确定它是如何完成的。
众所周知,0 地址(标记为宏“NULL”)是非法访问的。我想知道操作系统(比如linux)如何确定何时可以访问代码中某处的空地址,而不必访问代码中的每个指针地址?我认为它与信号有关,特别是与“sigsegv”信号有关。
但我不确定它是如何完成的。
指针指的是虚拟地址空间。在虚拟地址空间中,每一页内存都可以映射到真实的物理内存。操作系统为每个进程单独处理此映射。
当您通过指针访问内存时,CPU 会查看指针指定的虚拟地址的映射,并检查后面是否有真实的物理内存。根据您正在尝试的操作,执行额外的检查以验证您对该内存块具有读取或写入访问权限。
如果没有映射到该地址的内存,CPU 将生成硬件中断。操作系统捕获该中断并 - 通常 - 为调用进程发出 sigsegv 信号。
包含地址的零页NULL
通常被有意地保留为未映射,因此通常由编程错误导致的 NULL 指针访问很容易被捕获。
首先空指针访问不一定是无效的。通常,操作系统的程序加载器或链接器(取决于系统)设置进程以便不映射虚拟地址空间中的最低页面。
许多这样做的系统还允许应用程序映射第一页,使空引用有效。
检查 NULL 指针的方式与检查所有其他内存地址的方式相同:通过 CPU 的逻辑地址转换。
每次处理器访问内存(忽略缓存)时,它都会在进程的页表中查找地址。如果没有相应的条目,则处理器触发访问错误(在 Unix 变体中,该错误被转换为信号)。
如果页表中有地址条目,则处理器检查该页允许的访问。如果您处于用户模式并尝试访问受内核保护的页面,则会触发错误。如果您尝试写入只读页面,则会触发错误。如果您尝试执行不可执行的页面,则会触发错误。
这是一个相当冗长的话题。如果您想了解有关该主题的更多信息,您需要了解逻辑内存转换(有时是错误命名的虚拟内存)。
当指针被取消引用或访问无效地址Memory Management Unit
时,它在异常触发中起着关键作用。NULL
MMU
在每次访问时执行的正常虚拟到物理内存映射过程中RAM
,未定义的地址根本不会在MMU descriptors
. 如果发生在操作系统内核空间中,或者只是在用户空间域中进行进程终止和清理,这可能会产生灾难性的后果。
...操作系统(比如linux)如何确定何时可以访问代码中某处的空地址,而不必访问代码中的每个指针地址?
好吧,如果不访问指针,操作系统就无法确定 NULL 取消引用。来自wiki的分段错误:
在计算中,分段错误(通常简称为 segfault)或访问冲突是由具有内存保护的硬件引发的故障,通知操作系统(OS)内存访问冲突;在 x86 计算机上,这是一般保护故障的一种形式。作为响应,操作系统内核通常会执行一些纠正措施,通常通过向进程发送信号将故障传递给有问题的进程......
内存访问冲突是运行时事件,除非存在无效访问,否则操作系统不会向进程发出信号。
FWIW,允许进程访问为其分配的内存(在虚拟地址空间中)。分配的虚拟地址空间之外的任何地址,如果被访问,将产生一个错误(通过 MMU),进而产生分段错误。
TL;DR - SIGSEV 是在遇到 NULL 指针取消引用时生成的,而不是在那之前。此外,操作系统本身不会检测到错误访问,而是由内存管理单元通过引发故障通知操作系统。
Linux 从硬件获得这种支持。处理器被告知各个内存区域的用途及其可用性。如果访问“不可用”的内存区域,处理器会通知操作系统该问题并且操作系统会通知应用程序。
这意味着两件事:
换句话说,如果您的指针指向“可用”内存的任何位置,那么硬件单元将无法识别问题。