Linux 可执行程序的分段结构

说明

  • Linux 可执行程序是以ELF文件格式存储的,ELF格式是分段的;Linux系统采用段式内存管理架构,二进制程序加载进内存后内存分布也是分段的。
  • windows系统也是类似的。

个人理解

  • 分段是由编译器和操作系统实现,编译时编译器将不同类型的元素存储到相应的段,以区分处理,利于管理和加快操作效率。
  • 分段行为不是固定的,不同编译器,不同平台可能有细微差别,虚拟内存和硬盘都是连续的。
  • 程序由数据和处理两部分组成,处理部分(代码)编译后为二进制指令(代码段)比较固定,而数据部分(变量)有多种形式,例如:全局变量,局部变量,static变量,const变量等等,为了实现这些需求和加快操作效率,因此数据段有多种分段类型。

常见段

代码

  • 段名:代码段(text段)
  • 编译后生成给CPU执行的机器指令,一个程序只有一个代码段,只读,防止程序由于意外事故而修改自身指令。
  • 该段也有可能包含一些只读的常量,例如字符串常量,const修饰的变量(各种单片机的编译器会这样存储),由编译器决定。

变量

全局变量(生命周期等于整个程序执行期)

  • 全局变量在编码时已经固定了,占用内存大小和值已确定,因为这些特性,全局变量可以在编译时就准备好,不需要等到运行时再去逐个分配内存设置值。
  • 这里说的全局变量包括 全局变量,static的全局变量,static的局部变量。
  • 全局变量由于初始值的不同,有以下两种情况。

已赋不为0的初始值

  • 段名:已初始化数据段(data段)
  • 初值不为0,编译器简单处理认为数据都不同,需要逐个变量保存到二进制程序文件中,程序运行时直接将整段数据拷贝进内存。
  • 个人理解:编译器可以进一步优化,相同的值可以只存一份。
  • 该段保存了程序中所有赋了初值并且初值不为0的全局变量,包括全局变量,static的全局变量,static的局部变量。
  • 如果该段数据较多,会导致程序二进制文件非常大,如下:
int ar[300000] = {1}; //将全部存储在data段,虽然值都是一样的
static int a = 10; //保存在data段

void test(){
    static int b = 10; 
}

未赋初值或者初值为0

  • 段名:未初始化数据段(bss段)
  • 由于可以将这些变量的初值处理成一样的,都设置为0,二进制程序文件就没必要存储这些值,只需要记录该段的首地址和段长度,程序运行加载进内存时再按这些参数申请和格式化内存就好。
  • 该段保存程序中没有进行初始化或者初始化为0的全局变量;例如:
int a; 
int b = 0;
static int a;
static int a = 0;

void test(){
    static int b;
    static int b = 0;
}

局部变量(生命周期由系统控制)

  • 段名:栈。
  • 由于局部变量不是固定的,无法在编译时进行处理。
  • 增长方向:自顶向下增长;普通的局部变量以及每次函数调用时所需要保存的信息(返回地址;环境信息)。
  • 栈变量最大的特征是:栈变量都应该是临时变量,出栈后内存就非常可能被覆盖。

动态变量(生命周期由程序员控制)

  • 段名:堆。
  • 该段的大小并不固定,可动态扩张或缩减。
  • 因为动态变量并不是固定的,无法在编译时进行处理。
  • 动态存储区,是向高地址扩展的数据类型,是自下向上的扩展方式,动态内存分配的内存区域。
  • 堆变量最大的特征是:大内存变量或者永久变量,临时小内存变量不应该放在堆内存中,应该放在栈内存中。

只读变量

  • 段名:只读变量区域(rodata段)
  • 只读的内存区域,const修饰的常量,以及常量字符串保存在该区域。例如:
const int a = 100; 
char *b = "hello world"。 //hello world保存在该段,但是变量b不是。

存储时形态

  • 二进制程序中不包含堆,栈需要动态管理的段,程序加载进内存才会产生。

运行时形态

  • 每个进程都独自拥有4GB的虚拟内存空间,使用时再由系统转换成物理内存地址,该转换对用户是透明不可见的,因此时常讲的内存空间都是虚拟内存空间。
  • 程序的代码段和一些全局变量是固定的,不会改变的,编译时会将代码段和这些变量的虚拟内存地址确定,因此每次运行都是固定的。
  • Linux下C程序的内存分布图如下:
  • C程序将内存分为以下5部分,地址从低到高排列:
    Linux 可执行程序的分段结构_第1张图片

查看段信息

size 命令

  • size命令的作用是:显示一个ELF格式文件(可执行文件或者链接库文件)非运行状态下的各个段的大小(字节占用)。
  • 例如:
xxx@$ size sample_ipc
   text	   data	    bss	    dec	    hex	filename
 148613	 639392	 139276	 927281	  e2631	sample_ipc
 * text 代码段的大小是148613	字节
 * data 段的大小是 639392	字节
 * bss 段的大小是139276字节
 * dec 是 text + data + bss 的总大小的10机制表示,hex是其16进制表示
  • 注意:size 查看的ELF文件结构中的各段字节占用,所以没有堆和栈大小。

你可能感兴趣的:(#,Linux,内核知识)