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

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.xdnf.cn/news/18050.html

相关文章:

  • rent8 安装部署教程之 Windows
  • Python采集微店商品详情 API 返回值说明,json数据返回
  • MySQL(多表查询练习)
  • 《嵌入式Linux应用编程(六):并发编程基础:多进程exec函数族及多线程基础》
  • swift多卡并行训练微调qwen3-8B
  • QT开发中QString是怎么转char*类型的
  • ARM Cortex-M7 Thread Mode与Handler Mode
  • 数据结构:严格二叉树 (Strict Binary Tree)
  • PyTorch的安装-CPU版本或者GPU安装有什么区别吗
  • Unity_导航网格
  • 我的第一个音乐元素浏览项目上传至Github啦!
  • MyBatis 与 MyBatis-Plus 的区别
  • STM32L051同时处理Alarm A和Alarm B中断
  • SSH协议的GIT转换
  • 系统介绍pca主成分分析算法
  • flutter开发(二)检测媒体中的静音
  • Day59--图论--47. 参加科学大会(卡码网),94. 城市间货物运输 I(卡码网)
  • 【DDIA】第二部分:分布式数据
  • 应用层协议——HTTP
  • 抽奖程序web程序
  • JavaScript 基础实战:DOM 操作、数据类型与常见需求实现
  • 项目管理工具
  • NPM 、 NPX
  • 清除 pnpm 缓存,解决不同源安装依赖包失败的问题
  • electron之win/mac通知免打扰
  • 【R语言】R 语言中 gsub 与正则表达式详解(含 POSIX 与 Perl 风格实例)
  • 汽车电子:现代汽车的智能核心
  • [激光原理与应用-287]:理论 - 波动光学 - 电磁波既能承载能量,又能承载信息?
  • 【软件设计模式】前置知识类图、七大原则(精简笔记版)
  • Spark 运行流程核心组件(二)任务调度