1、程序的段
程序由不同的段构成(代码段,数据段)
- 程序的静态特征就是指令和数据
- 程序的动态特征就是执行指令处理数据
程序文件的一般布局
程序文件未运行时
代码段(.text)
- 源代码中的可执行语句编译后进入代码段
- 代码段在有内存管理单元的系统中具有只读属性
- 代码段的大小在编译结束后就已经固定(不能动态改变)
- 代码段中可以包含常量数据(如:常量字符串)
数据段(.data,.bss,.rodata)
- 数据段用于存放源代码中具有全局生命期的变量
★ .bss:存储未初始化(初始化为0)的变量
★ .data:存储具有非0初始值的变量,有时也会将字符串常量放这里
★ .rodata:存储const关键字修饰的变量,字符串常量
注:.bss段只是为未初始化的全局变量和局部静态变量预留位置而已,它并没有内容,所以它在文件中不占空间
面试中的小问题
同是全局变量和静态变量,为什么初始化的和未初始化的保存在不同段中?
答:程序加载后,.bss段中的所有内存单元被初始化为0,将程序文件中.data段相关的初始值写入对应内存单元
编程实验
关键段的分析 test.c
int dt_main()
{
return 0;
}
char g_no_value;
int g_value = 1;
char g_str[] = "wss";
const int g_c = 3;
int dt_main()
{
static char c_no_value;
static int c_value = 2;
return 0;
}
通过objdump,可以看到.data段对应变量的值
2、堆、栈、内存映射段
程序中的栈
- 程序中栈的本质是一片连续的内存空间
- SP寄存器作为栈顶“指针”实现入栈操作和出栈操作
栈的深入理解
- 中断发生时,栈用于保存寄存器的值
- 函数调用时,栈用于保存函数的活动记录(栈帧信息)
- 并发编程时,每一个线程拥有自己独立的栈
程序中的堆(Heap)
- 堆是一片“闲置"的内存空间,用于提供动态内存分配
- 堆空间的分配需要函数支持(malloc)
- 堆空间在使用结束后需要归还(free)
内存映射段(Memory Mapping Segment)
- 内核将硬盘文件的内容直接映射到内存映射段(mmap)
- 动态链接库在可执行程序加载时映射到内存映射段
- 程序执行时能够创建匿名映射区存放程序数据
内存映射文件原理简介
- 将硬盘上的文件数据逻辑映射到内存中(零耗时)
- 通过缺页中断进行文件数据的实际载入(一次数据拷贝)
- 映射后的内存的读写就是对文件数据的读写
地址空间分布图
0~0x08048000是操作系统使用的,中间空白区域是因为堆,栈,内存映射段的起始地址是随机的(为了安全)
3、小结
各个段的作用
- 堆栈段在程序运行后才正式存在,是程序运行的基础
- .bss段存放的是未初始化的全局变量和静态变量
- .text段存放的是程序中的可执行代码
- .data段保存的是已经初始化了的全局变量和静态变量
- .rodata段存放程序中的常量值
程序术语的对应关系
- 静态存储区通常指程序中的.bss和.data段
- 只读存储区通常指程序中的.rodata段
- 局部变量所占空间为栈上的空间
- 动态空间为堆中的空间
- 程序可执行代码存放于.text段
小结
- 程序编码在编译后对应可执行程序中的不同存储区
- 程序和进程不同,程序静态概念,进程是动态概念
- 堆栈段是程序运行的基础,只存在于进程空间中
- 程序可执行代码存放于.text段,是只读的
- .bss和.data段用于保存全局变量和静态变量