静态存储区(Static Storage Area)的总结
普通的全局变量未初始化,编译阶段放在
com
段,链接完后放在bss
段
在32位系统中,内核空间为1GB(地址范围:0xC0000000-0xFFFFFFFF),用户空间为3GB
高端内存(HIGHMEM)是32位系统访问超过896MB物理内存的解决方案,其虚拟地址范围位于内核空间的最高128MB(0xF8000000-0xFFFFFFFF)
低端内存 lowmem 映射:在 32 - bit Linux 系统中,物理内存的前 896MB 通常被直接映射到内核虚拟地址空间,这样内核可以方便地访问这些内存区域。
高地址 0xFFFFFFFF
┌──────────────────┐
│ 内核空间 │
├──────────────────┤ 0xC0000000
│ 栈区 │ ↓ 自动增长
├──────────────────┤
│ mmap区域 │ (共享库/匿名映射)
├──────────────────┤
│ 堆区 │ ↑ 通过brk扩展
├──────────────────┤
│ .bss │ 未初始化全局变量
├──────────────────┤
│ .data │ 已初始化全局变量
├──────────────────┤
│ .rodata │ 只读数据段
├──────────────────┤
│ .text │ 代码段
└──────────────────┘
低地址 0x00000000
用户空间(3GB)
- 代码段(.text):0x08048000-0x080xxxxx
- 数据段:
┌─────────────┐
│.data │ 初始化的全局变量
│.rodata │ 常量数据
│.bss │ 未初始化变量
└─────────────┘ - 堆区:通过brk扩展,最大可达≈2.9GB
- mmap区域:动态库/共享内存映射
内核空间(1GB)
- 低端内存:直接映射物理内存的896MB
- vmalloc区:0xF0000000-0xFFFEFFFF
- 高端内存:物理内存>896MB时的映射区
# 查看进程内存映射
cat /proc/$$/maps
# 查看系统内存布局
dmesg | grep -i "virtual kernel memory layout"
错误位置 | 错误表述 | 正确值 |
---|---|---|
vmalloc区域 | “120m-8m-8k” | 32位系统典型范围:0xF0000000-0xFFFEFFFF(约240MB) |
栈空间限制 | “最多能向下拓展8m” | 默认栈大小8MB(可通过ulimit -s 修改) |
堆地址范围 | “堆区能向上拓展将近3g” | 实际可用堆空间 ≈ 3GB - (text+data+bss+mmap区域) |
静态存储区(Static Storage Area)的总结如下:
静态存储区详细总结
1. 内存段划分
内存段 | 存储内容 | 生命周期 | 初始化方式 |
---|---|---|---|
.text 段 | 可执行代码(机器指令) | 程序启动到终止 | 编译器生成 |
.rodata 段 | 只读数据(常量字符串、const全局变量) | 程序启动到终止 | 显式初始化 |
.data 段 | 已初始化的全局变量/静态变量(初值非零) | 程序启动到终止 | 显式初始化 |
.bss 段 | 未初始化的全局变量/静态变量(默认初始化为0) | 程序启动到终止 | 自动零初始化 |
2. 核心特性对比
特性 | .text段 | .rodata段 | .data段 | .bss段 |
---|---|---|---|---|
可写性 | ❌ 只读 | ❌ 只读 | ✔️ 可写 | ✔️ 可写 |
磁盘占用 | ✔️ 占用 | ✔️ 占用 | ✔️ 占用 | ❌ 不占用 |
内存映射方式 | 直接加载 | 直接加载 | 直接加载 | 动态清零 |
地址增长方向 | 固定地址 | 固定地址 | 固定地址 | 固定地址 |
优化特性 | 可共享 | 可共享 | 进程私有 | 进程私有 |
3. 技术细节说明
.text
段(代码段)
- 存储编译后的机器指令
- 具有
r-x
内存权限(可读、可执行,不可写) - 多个进程实例共享同一物理内存(写时复制机制)
.rodata
段(只读数据段)
- 包含字符串常量、const修饰的全局变量
- 任何写操作会触发段错误(SIGSEGV)
- 编译器自动将字符串常量放入此段
.data
段(已初始化数据)
- 存放显式初始化的全局/静态变量
- ELF文件中实际存储初始化值
- 示例:
int global = 10; static int s = 20;
.bss
段(未初始化数据)
- 存放未初始化或初始化为0的全局/静态变量
- 程序加载时由内核自动填充零值
- ELF文件仅记录所需空间大小,不占实际磁盘空间
4. 验证方法
查看段信息
# 使用objdump查看段布局
objdump -h program | grep -E '\.text|\.data|\.bss|\.rodata'# 查看运行时内存映射
cat /proc/self/maps
代码验证示例
#include <stdio.h>const int const_val = 10; // .rodata
int init_val = 20; // .data
int uninit_val; // .bssint main() {printf(".text @ %p\n", main); // 代码段地址printf(".rodata @ %p\n", "Hello"); // 字符串常量地址printf(".data @ %p\n", &init_val); // 已初始化数据地址printf(".bss @ %p\n", &uninit_val); // 未初始化数据地址return 0;
}
5. 关键注意事项
-
静态变量的初始化
- C语言规定未初始化的全局/静态变量默认初始化为0
- C++中类静态成员变量需要单独定义(否则导致链接错误)
-
内存权限保护
-
尝试修改
.text
或.rodata
段内容会触发段错误(SIGSEGV) -
使用
mprotect()
可临时修改权限(需root权限)
-