1. 用户空间内存布局概览
以下是典型的 Linux 64 位用户空间内存布局(从低地址到高地址):
地址范围 | 区域名称 | 描述 |
---|---|---|
0x0000000000400000 | 程序代码区(Text Segment) | 存储可执行代码(函数体的二进制代码)。 |
0x0000000000600000 | 只读数据区(.rodata) | 存储常量数据(如字符串常量)。 |
0x0000000000601000 | 数据区(Data Segment) | 存储已初始化的全局变量和静态变量(.data 段)。 |
0x0000000000602000 | BSS 区(.bss) | 存储未初始化的全局变量和静态变量(初始化为 0 )。 |
0x00007ffff7a00000 | 堆区(Heap) | 动态分配的内存区域,由 malloc 、calloc 、realloc 等函数管理。 |
0x00007ffffffde000 | 共享库和内存映射区 | 存储共享库(如 libc.so )和内存映射文件(如 mmap 分配的内存)。 |
0x00007ffffffff000 | 栈区(Stack) | 存储局部变量和函数调用的上下文(如返回地址、参数等)。 |
0x0000800000000000 | 内核空间 | 用户空间和内核空间的分界线,用户程序无法直接访问内核空间。 |
2. 各区域的详细说明
(1)程序代码区(Text Segment)
-
地址范围:
0x0000000000400000
开始。 -
内容:存储可执行代码(函数体的二进制代码)。
-
属性:只读,不可修改。
(2)只读数据区(.rodata)
-
地址范围:紧邻代码区。
-
内容:存储常量数据(如字符串常量)。
-
属性:只读,不可修改。
(3)数据区(Data Segment)
-
地址范围:紧邻只读数据区。
-
内容:
-
.data
段:存储已初始化的全局变量和静态变量。 -
.bss
段:存储未初始化的全局变量和静态变量(初始化为0
)。
-
-
属性:可读写。
(4)堆区(Heap)
-
地址范围:从数据区向上增长。
-
内容:动态分配的内存(如
malloc
、calloc
、realloc
分配的内存)。 -
属性:可读写,由程序员手动管理。
(5)共享库和内存映射区
-
地址范围:位于堆区和栈区之间。
-
内容:
-
共享库(如
libc.so
)。 -
内存映射文件(如
mmap
分配的内存)。
-
-
属性:可读写,由操作系统管理。
(6)栈区(Stack)
-
地址范围:从高地址向下增长。
-
内容:
-
局部变量。
-
函数调用的上下文(如返回地址、参数等)。
-
-
属性:可读写,由编译器自动管理。
(7)内核空间
-
地址范围:
0x0000800000000000
及以上。 -
内容:内核代码和数据。
-
属性:用户程序无法直接访问。
3. 内存布局示例
以下是一个简单的 C 程序及其内存布局的示例:
#include <stdio.h>
#include <stdlib.h>
int global_var = 10; // 存储在 .data 段
int uninitialized_var; // 存储在 .bss 段
int main()
{
int local_var = 20; // 存储在栈区
int *heap_var = (int *)malloc(sizeof(int)); // 存储在堆区
*heap_var = 30;
printf("global_var address: %p\n", (void *)&global_var);
printf("uninitialized_var address: %p\n", (void *)&uninitialized_var);
printf("local_var address: %p\n", (void *)&local_var);
printf("heap_var address: %p\n", (void *)heap_var);
free(heap_var);
return 0;
}
运行结果可能如下:
global_var address: 00007ff664a79000
uninitialized_var address: 00007ff664a7d030
local_var address: 00000000005ffec4
heap_var address: 0000000000af4700
-
global_var
和uninitialized_var
位于数据区(.data
和.bss
段)。 -
local_var
位于栈区。 -
heap_var
位于堆区。
4. 内存布局的查看工具
-
pmap
:gdb ./a.out (gdb) info proc mappings
查看进程的内存映射。pmap <pid>
-
/proc/<pid>/maps
:
查看进程的详细内存布局。cat /proc/<pid>/maps
-
gdb
:
调试时查看内存布局。
5. 总结
-
Linux 64 位用户空间的内存布局包括代码区、数据区、堆区、栈区等。
-
各区域的功能和属性不同,由操作系统和编译器共同管理。
-
理解内存布局有助于编写高效、安全的程序,并更好地调试和分析程序行为。