运行时的数据结构
段
- 目标文件和可执行文件有几种不同格式,在大多数Svr4实现中采用一种叫做ELF(原意位”可扩展链接器格式“、现在代表
”可执行文件和链接格式“)。其他系统中可执行文件格式是COFF(普通目标文件格式)。 - 所有不同格式具有一个共同概念”段(segments)“。就目标文件而言它们是二进制文件中简单的区域,里面保存了某种特
定类型(如符号表条目)相关信息。术语section是ELF文件中最小组织单位,一个段包含几个section
此处的段和x86架构里的段不同:
在UNIX中,段表示一个二进制文件相关的内容块;
在x86架构中,地址空间并非一个整体,而是分成一些64K大小的区域,称为段
- 当一个可执行文件中运行size命令时,会告诉你这个文件中的三个段(文本段text、数据段data、bss段)的大小。检查
可执行文件的另一个方法是使用nm或dump工具(详见segments_information):
BBS(Block Started Symbol由符号开始的块)段只保存没有值的变量,所以它并不需保存这些变量的映像。运行时所需
的BSS段的大小记录在目标文件中,但BSS段(不像其他段)并不占据目标文件的任何空间。
数据段(data):存放初始化后的全局变量和静态变量(局部变量并不进入.out文件,它们在运行时创建)。文本段(text):存放可执行文件的指令(一般使用mmap()系统调用)。
.out
进程的地址空间
最高内存地址
________________________
| 堆栈段 | 栈:局部变量、临时数据、传递到函数的参数等
| | | 堆:malloc().
a.out | | | 过程活动记录
______________________ | |/ |
| a.out神奇数字 | . 空洞 .
|______________________| . .
| a.out其他内容 | . .
| | |________________________|
|______________________| | BBS | 未初始化的数据
| BSS段所需的大小 |--------------|________________________|
|______________________| | data |
| data数据段: | | | 经过初始化的数据
| 经过初始化的全局变量 |--------------| |
| 还还有静态变 | | |
| | | |
|______________________| |________________________|
| text文本段: |--------------| text | 指令
| 可执行文件指令 | | |
| | |________________________|
|______________________| |_______未映射区域_________| 捕捉使用空指针和小整型值的指针引用内存情况
函数调用:过程活动记录
活动记录:
______________________
| 局部变量 |
|______________________|
| 参数 |
|----------------------|
| 静态链接 | (用于上层引用,C中不使用)
|______________________|
| 指向先前结构的指针 |
|----------------------|
| 返回地址 | (前一个活动记录的地址)
|______________________|
static关键字
- 对堆栈怎样实现函数调用的描述页同样解释了为何不能从函数中返回一个指向该函数局部自动变量的指针。
char * favorite_fruit ()
{
char deciduous[] = "apple";
return deciduous;
}
当进入该函数时,自动变量deciduous在堆栈中分配。函数结束后,变量不复存在,它所占用的堆栈空间被回收,且随时可能被覆盖。
这样指针就失去了有效性(引用不存在的东西)被称为”悬垂指针“————它们并不引用有用的东西,而是悬在地址空间。如果想返回一个
指向在函数内部定义的变量的指针时,要把那个变量声明为static
。这样就能保证该变量被保存在数据段而不是堆栈中,该变量的
生命周期和该程序一样长,定义该变量的函数退出时,该变量的值依然保持。函数下次进入时该值依然有效。