关于程序在linux系统运行时内存的分配情况,我之前一直都不是很明白,花了一天的时间查了写资料,把内容和总结发上来

在多任务操作系统中,每个进程都运行在属于自己的内存沙盘中。这个沙盘就是虚拟地址空间,在32位模式下它是一个4GB的内存地址块。在Linux系统中, 内核进程和用户进程所占的虚拟内存比例是1:3,根据需要将其映射到物理内存。

虚拟地址通过页表(Page Table)映射到物理内存,页表由操作系统维护并被处理器引用。内核空间在页表中拥有较高特权级,因此用户态程序试图访问这些页时会导致一个页错误(page fault)。在Linux中,内核空间是持续存在的,并且在所有进程中都映射到同样的物理内存。内核代码和数据总是可寻址,随时准备处理中断和系统调用。与此相反,用户模式地址空间的映射随进程切换的发生而不断变化。

Linux进程在虚拟内存中的标准内存段布局如下图所示:

其中多个线程共享一个堆(heap),而每一个线程都有独立的栈(stack)

也就是说进程部分分段存储内容如下1 栈(stack)

栈又称堆栈,由编译器自动分配释放,行为类似数据结构中的栈(先进后出)。堆栈主要有三个用途:

为函数内部声明的非静态局部变量(C语言中称“自动变量”)提供存储空间。

记录函数调用过程相关的维护性信息,称为栈帧(Stack Frame)或过程活动记录(Procedure Activation Record)。它包括函数返回地址,不适合装入寄存器的函数参数及一些寄存器值的保存。除递归调用外,堆栈并非必需。因为编译时可获知局部变量,参数和返回地址所需空间,并将其分配于BSS段。

临时存储区,用于暂存长算术表达式部分计算结果或alloca()函数分配的栈内内存。2 堆(heap)

堆用于存放进程运行时动态分配的内存段,可动态扩张或缩减。堆中内容是匿名的,不能按名字直接访问,只能通过指针间接访问。当进程调用malloc(C)/new(C++)等函数分配内存时,新分配的内存动态添加到堆上(扩张);当调用free(C)/delete(C++)等函数释放内存时,被释放的内存从堆中剔除(缩减) 。

使用堆时经常出现两种问题:1) 释放或改写仍在使用的内存(“内存破坏”);2)未释放不再使用的内存(“内存泄漏”)。当释放次数少于申请次数时,可能已造成内存泄漏。泄漏的内存往往比忘记释放的数据结构更大,因为所分配的内存通常会圆整为下个大于申请数量的2的幂次(如申请212B,会圆整为256B)。

注意,堆不同于数据结构中的”堆”,其行为类似链表。3 BSS段

BSS(Block Started by Symbol)段中通常存放程序中以下符号:

未初始化的全局变量和静态局部变量

初始值为0的全局变量和静态局部变量(依赖于编译器实现)

未定义且初值不为0的符号(该初值即common block的大小)4 数据段(Data)

数据段通常用于存放程序中已初始化且初值不为0的全局变量和静态局部变量。数据段属于静态内存分配(静态存储区),可读可写。5 代码段(text)

代码段也称正文段或文本段,通常用于存放程序执行代码(即CPU执行的机器指令),常量和const修饰的变量(只读)。一般C语言执行语句都编译成机器代码保存在代码段。通常代码段是可共享的,代码段通常属于只读,以防止其他程序意外地修改其指令(对该段的写操作将导致段错误),

每一个进程在在Linux系统执行时,内存都是这样的分布情况,了解了进程执行过程中的内存分布情况,我们就对自己写的代码,有更深的理解。

相关文章