IA-32的故事

从Segmentation fault说起

相信你一定写过一些触发了Segmentation fault(段错误)的程序, 例如数组访问越界, 空指针引用等等, 这些错误的共同点是访问了非法的内存区域. 我们很容易在Linux下编写一个触发段错误的程序:

#include <stdio.h> int main() { printf("%d\n", *(int *)NULL); return 0; }

编译运行后, 你会看到屏幕上输出了

Segmentation fault

段错误还有其它的类型, 例如

int main() { asm volatile ("cli"); while(1); return 0; }

编译运行后, 你同样会看到段错误的信息. 这个恶意程序试图执行用于关中断的指令, 如果执行成功, 操作系统将无法响应外部中断, 除非恶意程序主动放弃执行, 它将独占整个系统的所有资源; 从用户的角度来看, 他会看到电脑死机了. 因此, cli指令不应该让一般的程序随意执行, 要执行它需要有一定的权限. 上述恶意程序因为执行了无权限的操作, 而被操作系统杀死, 从而保证系统的安全.

那么, 操作系统究竟是如何知道一个程序执行了非法操作(非法访问内存, 执行非法指令等)? 这是因为现代的CPU带有保护机制, 当CPU捕获到这些非法操作的时候, 它会抛出异常通知操作系统, 操作系统会进行相应的处理, 一般是杀死那个执行非法操作的程序. 如果你使用过Online Judge, 应该会看到过Run-time Error的信息, 这是因为Online Judge的守护进程知道你提交的程序要被杀死了.

再深入一步, CPU是怎么捕获这些非法操作的? 答案就在接下来的故事中. 在这之前, 你可能会问"为什么类似cli的非法操作也会称为段错误?" 实际上这是有历史原因的, 在以前的CPU架构中没有这么强大的保护功能, 一般的非法操作都和分段机制有关(例如内存访问超越了段界限), 因此被称为Segmentation fault. 但这种称呼直到分页机制诞生之后也没有改变, 于是便一直沿用至今. 更多关于Segmentation fault的内容可以阅读这里.

results matching ""

    No results matching ""