Linux C语言内存管理完全指南
与Java、Python等带自动垃圾回收的高级语言不同,C语言将内存的控制权完全交给程序员,这既是其强大之处,也是最容易出错的地方
一、Linux C程序的虚拟内存布局
当一个C程序在Linux上运行时,操作系统会为其创建一个独立的虚拟地址空间。
32位系统为4GB(用户态3GB + 内核态1GB)。
标准 x86_64 Linux(48 位虚拟地址):用户态 128TB + 内核态 128TB = 256TB;
ARM64 Linux(48 位虚拟地址):用户态 256TB + 内核态 256TB = 512TB;
支持 5 级页表的 x86_64 Linux(57 位虚拟地址):用户态 64PB + 内核态 64PB = 128PB。
内存布局全景图(低地址在下,标准教材风格)
各内存段详细解析
1.代码段(.text Segment)
存储:程序的可执行机器指令、字符串字面量、const修饰的全局常量
权限:只读+可执行,防止程序被恶意修改
特点:编译时确定大小,运行时不变;多个进程可共享同一份代码段(如多个ls进程共享同一个ls可执行文件)
2.初始化数据段(.data Segment)
存储:已初始化的全局变量和静态变量(包括函数内的静态变量)
权限:可读可写
特点:占用可执行文件的磁盘空间,程序加载时由内核复制到内存
3.未初始化数据段(.bss Segment)
存储:未初始化的全局变量和静态变量
特点:不占用可执行文件的磁盘空间,程序加载时内核自动将其全部初始化为0
优势:节省磁盘空间,例如一个大小为1MB的未初始化全局数组,在可执行文件中只占用4字节的大小记录
4.堆(Heap)
存储:程序运行时动态分配的内存
特点:大小不固定,可动态扩展或收缩;由低地址向高地址增长;由程序员手动管理(malloc/free)
底层实现:通过brk()和sbrk()系统调用调整堆顶指针,大内存分配使用mmap()
5.栈(Stack)
存储:函数的局部变量、函数参数、返回地址、寄存器上下文
特点:大小固定(Linux默认8MB,可通过ulimit -s修改);由高地址向低地址增长;由编译器自动管理
工作原理:函数调用时压栈,函数返回时弹栈,遵循"后进先出"原则
6.内核空间(Kernel Space)
存储:操作系统内核的代码和数据
权限:仅内核态可访问,用户态程序直接访问会触发段错误(Segmentation Fault)
作用:管理系统资源,为用户态程序提供系统调用接口
示例1:验证各内存段的地址分布
C #include#include// 全局变量 int global_init = 10;// .data段 int global_uninit;// .bss段 static int static_init = 20; // .data段 static int static_uninit;// .bss段 const int const_global = 30; // .text段(只读常量) int main() { // 局部变量(栈) int local = 40; static int func_static = 50; // .data段 // 动态分配内存(堆) int *heap_ptr = (int *)malloc(sizeof(int)); *heap_ptr = 60; // 字符串字面量(.text段,只读) const char *str = "Hello, Linux!"; printf("代码段地址:\n"); printf("函数main:%p\n", main); printf("字符串字面量:%p\n", str); printf("const全局变量:%p\n", &const_global); printf("\n数据段地址:\n"); printf("已初始化全局:%p\n", &global_init); printf("已初始化静态:%p\n", &static_init); printf("函数内静态:%p\n", &func_static); printf("\nBSS段地址:\n"); printf("未初始化全局:%p\n", &global_uninit); printf("未初始化静态:%p\n", &static_uninit); printf("\n堆地址:\n"); printf("malloc分配:%p\n", heap_ptr); printf("\n栈地址:\n"); printf("局部变量:%p\n", &local); free(heap_ptr); return 0; } |
编译运行:
Bash gcc memory_segments.c -o memory_segments ./memory_segments |
典型输出(地址会因系统不同而变化,但相对顺序固定):
开发板:
虚拟机: