1. 进程映像:内存中的程序执行蓝图
1.1 进程映像的组成与布局
当我们在终端输入./a.out运行程序时,操作系统会创建一个独特的"容器"来承载这个运行实例——这就是进程映像。它不仅仅是简单的代码拷贝,而是一个精心设计的结构化内存空间,包含程序运行所需的一切要素。
进程映像的典型布局遵循"低地址到高地址"的扩展原则,这种设计源于早期计算机的内存管理方式。现代Linux系统在x86-64架构下的进程地址空间示例如下:
code复制0x0000000000400000 代码段起始(text)
0x0000000000600000 数据段起始(data)
0x0000000000601000 BSS段结束
0x00007ffff7dd5000 堆起始地址(heap)
...
0x00007ffffffde000 栈顶地址(stack)
0xffffffffff600000 内核空间
每个内存段都有明确的访问权限控制,这是通过CPU的MMU(内存管理单元)和操作系统的页表共同实现的。我们可以通过pmap命令查看实际进程的内存映射:
bash复制$ pmap -x <pid>
Address Kbytes RSS Dirty Mode Mapping
00400000 4 4 0 r-x-- a.out
00600000 4 4 4 rw--- a.out
...
注意:在实际编程中,使用
mmap系统调用可以创建自定义的内存映射区域,这会动态改变进程映像的布局结构。
1.2 关键内存段详解
代码段(Text Segment)
这是存放机器指令的区域,具有只读和可执行属性。现代编译器会将字符串常量也放在这个区域,因此以下代码中的字符串实际上位于代码段:
c复制printf("Hello World"); // "Hello World"存储在代码段
多个相同程序的进程实例会共享同一份物理代码段,这是通过写时复制(Copy-On-Write)技术实现的。我们可以通过objdump查看二进制文件的代码段内容:
bash复制$ objdump -d a.out
数据段与BSS段
这两个段都用于存储全局变量,但存在关键区别:
- 数据段:存储显式初始化的变量(如
int g_val = 42;) - BSS段:存储未初始化或初始化为0的变量(如
int g_array[1000];)
BSS段的设计优化了可执行文件的大小——磁盘上只需记录BSS段的大小信息,无需存储实际的零值数据。通过size命令可以查看各段大小:
bash复制$ size a.out
text data bss dec hex filename
1526 544 8 2078 81e a.out
