当前位置: 首页 > news >正文

Linux程序内存布局分析

Linux程序内存布局分析


一、完整程序内存布局详解(Linux x86-64)

高地址 0x7FFFFFFFFFFFFF
+----------------------+ <-- 内核空间边界
| **内核空间**         | (操作系统内核代码/数据)
+----------------------+ <-- 0x7FFF80000000
| **栈 (Stack)**       | ↓ 向下增长
|   - 函数栈帧         |
|   - 局部变量         |
|   - 返回地址         | <-- RSP (栈指针寄存器)
+----------------------+
| **共享库映射区**      | (动态链接库加载位置)
|   - libc.so          |
|   - ld-linux-x86-64  |
+----------------------+
| **内存映射段**        | (mmap分配区域)
|   - 文件映射         |
|   - 匿名映射         |
+----------------------+
|          ↓           |
| **堆 (Heap)**        | ↑ 向上增长
|   - malloc/new分配区  | <-- brk/sbrk边界
+----------------------+ <-- 程序断点 (brk)
| **BSS 段**           | (未初始化全局数据)
|   - 未初始化全局变量  | 
|   - 初始化为0的变量   | 
+----------------------+
| **DATA 段**          | (已初始化全局数据)
|   - 初始化全局变量    |
|   - 静态变量         |
|   - 常量字符串       |
+----------------------+
| **RODATA 段**        | (只读数据)
|   - 字符串常量       |
|   - const全局常量     |
+----------------------+
| **TEXT 段**          | (代码段)
|   - 机器指令         |
|   - 函数代码         | <-- RIP (指令指针)
+----------------------+ 
| **保留区**           | (0x00000000-0x400000)
低地址 0x00000000000000

二、关键内存区域深度解析

1. 代码段 (TEXT)
  • 内容:编译后的机器指令
  • 权限r-x (可读/执行,不可写)
  • 特点
    • 所有进程共享同一物理副本(写时复制)
    • 包含函数入口点(_start, main
  • 实例
    $ readelf -S a.out | grep .text
    [13] .text  PROGBITS  0000000000401040 001040...
    
2. 数据段 (DATA/BSS)
段名存储内容ELF节名初始化方式
DATA显式初始化的全局/静态变量.data程序加载时从文件读取
BSS未初始化的全局/静态变量.bss内核初始化为零
RODATA只读常量.rodata程序加载时初始化
  • 实例代码
    int data_var = 42;          // DATA段
    const char *rodata = "ABC"; // RODATA段 (.rodata)
    static int bss_var;         // BSS段
    
3. 堆 (Heap)
  • 管理机制
    +-------------------+
    |  glibc的ptmalloc2  |  ← 用户态内存分配器
    +-------------------+
    |   brk()系统调用    |  ← 调整program break位置
    +-------------------+
    | 内核的vm_area_struct | ← 管理虚拟内存区域
    +-------------------+
    
  • 分配流程
    1. malloc(128) 检查线程本地缓存 (tcache)
    2. 未命中 → 检查fast bins/free lists
    3. 仍未命中 → 通过brk()mmap()扩展堆
4. 栈 (Stack)
  • 栈帧结构详解
    高地址
    +-------------------+
    |    参数n          | 
    |    ...            |
    |    参数1          |
    +-------------------+
    |    返回地址        | ← 调用结束后返回位置
    +-------------------+
    |    保存的EBP       | ← 当前栈帧基址
    +-------------------+
    |    局部变量1       |
    |    局部变量2       |
    |    ...            |
    +-------------------+
    |    对齐填充        | ← 栈对齐要求(16字节)
    +-------------------+ ← 当前栈顶 (RSP)
    低地址
    
  • 寄存器
    • RSP:栈顶指针寄存器
    • RBP:栈基址寄存器(可选使用)
    • RIP:下条指令地址

三、高级内存区域

1. 内存映射段 (Memory Mapping Segment)
  • 分配方式mmap()系统调用
  • 使用场景
    // 文件映射
    void *file_map = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0);// 匿名映射 (大内存分配)
    void *big_mem = mmap(NULL, 1<<30, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
    
  • 优势:独立于堆,直接由内核管理,适合大块内存
2. 线程栈
  • 特点
    • 每个线程有独立栈(默认2-10MB)
    • 通过pthread_create()创建
    • 位于内存映射段
    +---------------------+
    | 主线程栈 (8MB)       |
    +---------------------+
    | 线程1栈 (2MB)        |
    +---------------------+
    | 线程2栈 (2MB)        |
    +---------------------+
    | ...                |
    
3. 内核空间
  • 组成
    +---------------------+
    | 直接映射区           | ← 物理内存1:1映射
    +---------------------+
    | vmalloc区           | ← 不连续物理页映射
    +---------------------+
    | 持久内核映射区        | ← 高端内存映射
    +---------------------+
    | 固定映射区           |
    +---------------------+
    

四、实例分析:完整内存快照

C程序示例
#include <stdlib.h>
#include <stdio.h>int global_init = 10;     // DATA段
char *str = "Hello";      // RODATA段 (指针在DATA)
int global_uninit;        // BSS段void func(int param) {    // TEXT段int local = 20;       // 栈static int static_local = 30; // DATA段int *heap_var = malloc(sizeof(int)); // 堆printf("%s\n", str);  // 调用库函数 (共享库映射区)
}int main() {func(42);return 0;
}
运行时内存布局图示
0x7ffff7ffb000 +----------------------+| 主线程栈              ||   - main栈帧          ||   - func栈帧          ||     param=42          ||     local=20          |
0x7ffff7de0000 +----------------------+ <-- 共享库映射区| libc.so代码/数据      ||   printf实现代码      |
0x7ffff7a00000 +----------------------+ <-- 堆顶 (brk)| malloc分配的heap_var  |+----------------------+| ... (堆空间)          |
0x55555555a000 +----------------------+ <-- BSS段| global_uninit (初始0) |
0x555555559000 +----------------------+ <-- DATA段| global_init=10        || str (指向RODATA地址)  || static_local=30       |
0x555555558000 +----------------------+ <-- RODATA段| "Hello" 字符串常量     |
0x555555554000 +----------------------+ <-- TEXT段| func机器码            || main机器码            |
0x400000       +----------------------+

五、内存管理关键数字

参数典型值查看命令
栈默认大小8MB (Linux)ulimit -s
页大小4KBgetconf PAGE_SIZE
堆分配阈值128KB (glibc)mallopt(M_MMAP_THRESHOLD)
TEXT/DATA最大偏移2GB (32位系统)编译链接参数控制
ASLR随机化范围±28位 (Linux)/proc/sys/kernel/randomize_va_space

六、高级话题:内存映射底层原理

虚拟内存到物理内存映射
虚拟地址空间          页表           物理内存
+---------------+   +-----------+   +---------------+
| 0x400000      | → | PTE       | → | 代码页1        |
| (TEXT)        |   +-----------+   +---------------+
+---------------+   +-----------+   +---------------+
| 0x600000      | → | PTE       | → | 数据页         |
| (DATA)        |   +-----------+   +---------------+
+---------------+   +-----------+   +---------------+
| 0x7fff0000    | → | PTE       | → | 栈页           |
| (Stack)       |   +-----------+   +---------------+
+---------------+   +-----------+   +---------------+
| 0x100000000   | → | PTE       | → | 堆页           |
| (Heap)        |   +-----------+   +---------------+
缺页中断处理流程
  1. CPU访问未映射的虚拟地址
  2. 触发缺页中断(Page Fault)
  3. 内核检查访问合法性
  4. 分配物理页/加载文件内容
  5. 更新页表项
  6. 返回用户态重新执行指令

七、诊断工具

  1. Linux 进程映射

    cat /proc/$PID/maps
    

    输出示例

    00400000-00401000 r-xp 00000000 08:01  /app        # TEXT
    00600000-00601000 r--p 00000000 08:01  /app        # DATA
    00601000-00602000 rw-p 00001000 08:01  /app        # BSS
    7ffff7a00000-7ffff7bc0000 rw-p 00000000 00:00 0    # Heap
    7ffff7bd0000-7ffff7bd1000 rw-p 00000000 00:00 0     # 匿名映射
    7ffffffde000-7ffffffff000 rw-p 00000000 00:00 0     # Stack
    
  2. Windows 工具

    • VMMap (SysInternals)
    • WinDbg !address 命令
http://www.dtcms.com/a/333120.html

相关文章:

  • ROS2 setup.bash和setup.sh区别差异
  • 【JAVA】实现word添加标签实现系统自动填入字段
  • Python匿名函数的具体用法
  • 堕落之主 单机+联机 全DLC 送修改器(Lords of the Fallen)免安装中文版
  • 【机器人-基础知识】ROS常见功能架构
  • 【JAVA高级】实现word转pdf 实现,源码概述。深坑总结
  • JavaScript(JS)DOM(四)
  • 提词器电脑版哪个好用?芦笋提词器优势评测与下载
  • 算法02 二进制与位运算
  • 解锁AI大模型:Prompt工程全面解析
  • 迭代器模式C++
  • Unity中 terriaria草,在摄像机拉远的时候就看不见了,该怎么解决
  • week1-[循环嵌套]蛇
  • 低资源语言翻译:数据增强与跨语言迁移学习策略
  • git疑问,暂时记录
  • “降碳30%+节能25%,园区智慧能源管理系统重塑示范园区竞争力
  • Flutter Form组件的基本使用
  • 【Python办公】Excel转json(极速版)-可自定义累加字段(如有重复KEY)
  • 力扣top100(day04-06)--贪心算法
  • LeetCode 53.最大子数组和:贪心算法下的连续子数组最优解
  • Android项目中Ktor的引入与使用实践
  • mlir clone
  • 【C#补全计划】事件
  • 【C#】 GridControl与GridView、容器和视图
  • Spring事务 概念 配置 隔离级别 脏读幻读不可重复读 传播行为
  • pyinstaller-从安装到高级使用
  • align-content 设置侧轴上的子元素的排列方式(多行)
  • Git代码版本管理
  • OpenCV---getStructuringElement 结构元素获取
  • 设计心得——如何架构选型