Linux 进程地址空间
一. 进程地址空间
- 内核空间 (Kernel Space)
- 作用:内核空间是操作系统内核使用的内存区域。这部分内存对用户进程不可见,只能由内核访问。
- 内容:内核空间包含内核代码、内核数据结构、设备驱动程序、系统调用接口等。
- 特点:内核空间通常位于内存地址空间的高地址处,以防止用户进程意外访问或篡改内核数据。
- 栈 (Stack)
- 作用:栈用于存储函数调用时的局部变量、函数参数和返回地址。
- 内容:每次函数调用时,栈帧(stack frame)会被压入栈中,包含局部变量、函数参数和返回地址。
- 特点:栈是自动管理的,随着函数调用和返回自动增长和收缩。栈的增长方向是从高地址向低地址增长。
- 共享区 (Shared Memory)
- 作用:共享区用于多个进程之间的数据共享。
- 内容:共享区内存可以被多个进程映射到各自的地址空间,允许多个进程访问相同的数据。
- 特点:共享区通常由操作系统提供,可以通过系统调用(如 shmget 和 shmat)创建和管理。
- 堆 (Heap)
- 作用:堆用于动态内存分配,程序可以通过 malloc、calloc、realloc 和 free 等函数在堆上分配和释放内存。
- 内容:堆内存通常用于存储动态分配的对象、数组等。
- 特点:堆的增长方向是从低地址向高地址增长。堆内存的管理由程序员负责,不当的管理可能导致内存泄漏。
- 数据段 (Data Segment)
- 作用:数据段用于存储全局变量和静态变量。
- 内容:数据段分为两个部分:已初始化数据区和未初始化数据区。
已初始化数据区 (Initialized Data Section):存储已初始化的全局变量和静态变量。
未初始化数据区 (BSS Section):存储未初始化的全局变量和静态变量。这些变量在程序启动时会被初始化为零。 - 特点:数据段的大小在编译时确定,程序启动时会被加载到内存中。
- 代码段 (Text Segment 或 Code Segment)
作用:代码段用于存储程序的机器指令。
内容:代码段包含编译后的二进制代码,即程序的可执行指令。
特点:代码段通常是只读的,以防止程序意外修改自己的代码。代码段的大小在编译时确定,程序启动时会被加载到内存中。
二. 虚拟地址空间与页表
2.1 虚拟地址空间
虚拟地址空间是操作系统为每个进程提供的一组虚拟地址。这些地址在进程看来是连续的,但实际上它们会被映射到物理内存的不同位置。虚拟地址空间的目的是使每个进程都认为自己独占了整个内存,从而简化内存管理和提高安全性。
2.2 页表
页表是操作系统中用于实现虚拟地址到物理地址翻译的关键数据结构。它帮助操作系统管理和映射进程的虚拟地址空间到物理地址空间。
2.3 页表的映射原理
虚拟地址由三部分组成:
- 页目录索引:0x123(10位)
- 页表索引:0x456(10位)
- 页内偏移量:0x678(12位)
地址翻译过程:
- 提取页目录索引:从虚拟地址中提取页目录索引。
- 查找页目录:使用页目录索引查找页目录中的对应条目,获取二级页表的基地址。
- 提取页表索引:从虚拟地址中提取页表索引。
- 查找页表:使用页表索引查找二级页表中的对应条目,获取物理页框地址。
- 组合物理地址:将物理页框地址与页内偏移量组合,形成最终的物理地址。