前两天和阿彬
扯淡的时候(注意不是扯蛋
),他说自己一个礼拜解决了三个Core dump,华丽丽之极,让我对其敬仰、膜拜之情如滔滔之江水,绵绵不绝
。
今天我也来谈一下Linux环境下开发时经常遇到的“Segmetation fault”的一点点个人看法,也算是抛砖引玉了。
(备注:我的glibc版本是2.12,GCC版本4.4.6,内核版本2.6.32-279)
Linux 上开发时最恼火的就是遇到“ Segmetation Fault ”错误。为什么这么说,很多人看到这个错误后心里第一反应是程序访问的非法的内存,导致其被操作系统强行终止。这固然没错,可这里有个比较模糊的概念了:什么叫“非法”的内存?
程序运行时,每个进程都有自己的虚拟地址,理论上说进程应该可以随便使用才对,为什么还会出现这个错误呢?这里就涉及到程序的装载过程及原理。
先澄清几个概念:
程序:一般是一组 CPU 指令的集合构成的文件,静态存储在诸如硬盘之类的存储设备上。
进程:当一个程序要被计算机运行时,就是在内存中产生该程序的一个运行时实例,我们就把这个实例叫做进程。
装载:上述从硬盘上的静态“程序”到内存中动态的“进程”之间的转变过程就叫做装载。往通俗里讲,就是启动一个进程。
本文的主要目的是在简单了解进程的内存布局的情况下,从装载的过程入手,深入了解一下 Segmetation Fault 在操作系统层面是如何产生的,以及程序开发过程中应该如何避免这样的错误。
众所周知 Linux 中可执行文件的格式是 ELF ,其实编译过程中的中间文件 *.o 文件、动态共享库 *.so 文件也是 ELF 格式的。在链接器看来,当它通过 *.o 或者配合 *.so 文件来生成可执行文件时,它对 ELF 格式的文件以链接视图 (Linking View) 进行看待。也就是说链接器以 Section 的形式来对待和处理 ELF 文件,诸如我们常见说的代码段 (.text) 、数据段 (.data 和 .bss) 等待概念。当程序最终需要被装载成进程时,装载器就出场了,装载器将可执行文件以装载视图 (Executive View) 进行看待。装载器将以 Segment 的形式来处理 ELF 文件。网上很多教程也是这样说的,大家可能还是理解的不是很明白,后面我们通过实例的方式将进一步向大家来澄清这两者的区别。
既然 *.o 、 *.so 和可执行文件都是 ELF 格式,那么链接器和装载器是如何区分它们的呢?
看一个简单的例子:
readelf –h 命令能够可以查看一个 EFL 文件的头部信息。因为 viewobj.o 是编译时的中间临时文件,所以它的“ Start of pgrogram headers ”和“ Number of program headers ”都为 0 ,说明他不是一个可执行文件。取而代之的是它有 9 个 section ,所以它有“ Start of section headers ”和“ Number of section headers ”都有数据。
再看一下动态共享库:
(备注:我的glibc版本是2.12,GCC版本4.4.6,内核版本2.6.32-279)
Linux 上开发时最恼火的就是遇到“ Segmetation Fault ”错误。为什么这么说,很多人看到这个错误后心里第一反应是程序访问的非法的内存,导致其被操作系统强行终止。这固然没错,可这里有个比较模糊的概念了:什么叫“非法”的内存?
程序运行时,每个进程都有自己的虚拟地址,理论上说进程应该可以随便使用才对,为什么还会出现这个错误呢?这里就涉及到程序的装载过程及原理。
先澄清几个概念:
程序:一般是一组 CPU 指令的集合构成的文件,静态存储在诸如硬盘之类的存储设备上。
进程:当一个程序要被计算机运行时,就是在内存中产生该程序的一个运行时实例,我们就把这个实例叫做进程。
装载:上述从硬盘上的静态“程序”到内存中动态的“进程”之间的转变过程就叫做装载。往通俗里讲,就是启动一个进程。
本文的主要目的是在简单了解进程的内存布局的情况下,从装载的过程入手,深入了解一下 Segmetation Fault 在操作系统层面是如何产生的,以及程序开发过程中应该如何避免这样的错误。
众所周知 Linux 中可执行文件的格式是 ELF ,其实编译过程中的中间文件 *.o 文件、动态共享库 *.so 文件也是 ELF 格式的。在链接器看来,当它通过 *.o 或者配合 *.so 文件来生成可执行文件时,它对 ELF 格式的文件以链接视图 (Linking View) 进行看待。也就是说链接器以 Section 的形式来对待和处理 ELF 文件,诸如我们常见说的代码段 (.text) 、数据段 (.data 和 .bss) 等待概念。当程序最终需要被装载成进程时,装载器就出场了,装载器将可执行文件以装载视图 (Executive View) 进行看待。装载器将以 Segment 的形式来处理 ELF 文件。网上很多教程也是这样说的,大家可能还是理解的不是很明白,后面我们通过实例的方式将进一步向大家来澄清这两者的区别。
既然 *.o 、 *.so 和可执行文件都是 ELF 格式,那么链接器和装载器是如何区分它们的呢?
看一个简单的例子:
readelf –h 命令能够可以查看一个 EFL 文件的头部信息。因为 viewobj.o 是编译时的中间临时文件,所以它的“ Start of pgrogram headers ”和“ Number of program headers ”都为 0 ,说明他不是一个可执行文件。取而代之的是它有 9 个 section ,所以它有“ Start of section headers ”和“ Number of section headers ”都有数据。
再看一下动态共享库:
在Linux下动态共享库被当作可执行文件来处理,虽然它不能单独执行,但某些应用程序的运行离不了它。
最后是可执行文件,这个就不用多说了,看图: