什么是Segment fault
本文介绍Segment fault的概念,并通过代码进行demo。
写一段代码,命名为segfault.c
:
#include <stdio.h>
#include <unistd.h>
int main() {
printf("PID of the process: %d\n", getpid());
char *p = "hello";
printf("addr of string: %p\n", p);
printf("Press [Enter] key to generate segment fault...\n");
getchar();
*p = 'H'; // 尝试对只读区域的内存进行写入操作时,会产生Segment fault
}
这段代码可以用来生成segment fault。因为”hello, world”这串字符,是一个常量,C的编译器会把它放到只读的内存区域里面。当试图修改只读区域的内存数据时,就会产生Segment fault。
因此,理解process的内存模型非常重要,要理解process的虚拟内存是如何由操作系统分配并使用的。下面这张图要在脑子里很清楚才可以:
这个图来自于这篇文章:
https://manybutfinite.com/post/anatomy-of-a-program-in-memory/
上面这篇文章里的内容要读透。
接下来编译上面这段代码:
$ cc segfault.c -o segfault
这样会生成可执行文件。然后我们来执行编译后的程序:
可以看到,这个程序产生了段错误并退出了。
我们可以使用MacOS下的vmmap
命令,查看process的内存状况。为了查看进程的内存使用状况,我们不可以让这个segfault
的程序退出。那么就跑起来这个程序,不按回车键,不让它退出即可:
$ ./segfault
PID of the process: 53545
addr of string: 0x109930f62
Press [Enter] key to generate segment fault...
此时我们可以使用vmmap
命令查看这个process,把对应的pid编号传给vmmap
指令即可:
$ vmmap 53545
此时你会看到非常详细的关于这个process的内存使用状况。因为输出的报告非常长,大家可以在自己的机器上跑一跑,看一下具体内容。这篇文章里关心的部分是这里:
注意上面截取的内存部分是Non-writable regions
,其中TEXT,也就是程序段的内存是:
__TEXT 0000000109930000-0000000109931000 [ 4K 4K 0K 0K] r-x/rwx SM=COW /Users/weli/Desktop/segfa
注意到上面的地址区间,再回过头看我们程序的输出:
addr of string: 0x109930f62
可以看到,hello, world
这串字符的地址,落在了上面的TEXT的Non-writeable
区间里面。所以,当我们试图往只读的内存区间写入数据的时候,就会产生segment fault了。
本文介绍了Segment fault的概念,但并没有介绍如何debug段错误的方法,后续会为大家介绍调试Segment fault的方法,也就是coredump的使用。