【Linux C基础】虚拟内存分布


前言

程序只是一段可以执行的代码文件,通俗讲在 linux 上就是一个可执行文件。当一个程序运行时就被称为进程,即进程是运行状态的程序。对32位处理器,虚拟内存空间为4G,每个进程都认为自己拥有4G的空间。虚拟内存又被划分为不同分区,用于存储不同类型的数据。


一、虚拟内存分布

1. 虚拟内存

对32位处理器,虚拟内存空间为4G,每个进程都认为自己拥有4G的空间,实际上,在虚拟内存对应的物理内存上,可能只对应的一点点的物理内存。进程得到的这4G虚拟内存是一个连续的地址空间(这也只是进程认为),而实际上,它通常是被分隔成多个物理内存碎片,还有一部分存储在外部磁盘存储器上,在需要时进行数据交换。
由于存在两个内存地址,因此一个应用程序从编写到被执行,需要进行两次映射。第一次是映射到虚拟内存空间,第二次时映射到物理内存空间。在计算机系统中,第两次映射的工作是由硬件和软件共同来完成的。承担这个任务的硬件部分叫做存储管理单元 MMU,软件部分就是操作系统的内存管理模块了。

参考资料:Linux C内存分配程序内存分布

2. 进程的虚拟内存分布

进程的内存空间按照 代码段(TEXT)、初始化数据段(DATA)、未初始化数据段(BSS)、堆(HEAP)、文件映射段(MMS)、栈(STACK)、内核科技(Kernel)由低到高分配,但是只增值到0xC0000000, 最后1G留给了内核。0 ~ 3G属于用户空间,3 ~ 4G为内核空间。

  1. 程序文件段:包括程序的二进制可执行代码,只读。
  2. 已初始化数据段:包括已初始化的全局变量和静态常量。
  3. 未初始化数据段:包括未初始化的静态变量和全局变量。
  4. 堆段:包括动态分配的内存,从低地址开始向上增长。
  5. 文件映射段:包括动态库、共享内存等,从低地址开始向上增长(跟硬件和内核版本有关)。
  6. 栈段:包括函数的参数值和局部变量、函数调用的上下文等。栈的大小是固定的,一般是8MB。当然系统也提供了参数,以便我们自定义大小;栈区是从高地址位向低地址位增长的。
  7. 内核空间:包含内核栈和内核的数据段,所以内存地址生长方向既有由低到高(内核数据段),也有由高到低(内核栈)。关于读写的特点,由内核进行读写,用户程序不可直接访问。
    进程的内存结构

3. 程序的内存分布

可执行程序在存储时(没有调入到内存)只有代码段(TEXT)、数据段(DATA)、未初始化数据段(BSS)3个部分。下图对比了可执行代码存储结构和运行时(进程)的内存结构对照:
程序与进程的内存结构差异

4. 内存分配方式

  • 静态和动态分配
  1. 静态分配:编译器在处理程序的源代码的时候由编译器分配
  2. 动态分配:程序在执行过程中由程序员自己分配,堆内存调用malloc库函数申请分配,栈内存使用alloca申请分配(仅在Linux下)。
  • 静态和动态分配的区别
  1. 静态对象的分配与释放由编译器自动处理;动态对象的分配和释放必须由程序员自己显示的管理,通过malloc 和free 两个函数来完成。
  2. 静态对象是有名字的变量,可以直接对其进行操作;动态对象是没有名字的变量,需要通过指针间接的对它进行操作。
段名存储内容分配方式生长方向读写特点运行态
代码段程序指令、字符串常量、虚函数表静态分配由低到高只读用户态
数据段初始化的全局变量和静态变量静态分配由低到高可读可写用户态
BSS段未初始化的全局变量和静态变量静态分配由低到高可读可写用户态
动态申请的数据动态分配(Linux malloc)由低到高可读可写用户态
映射段动态链接库、共享文件、匿名映射对象动态分配由低到高可读可写用户态
局部变量、函数参数与返回值、函数返回地址、调用者环境信息静态+动态(Linux alloca)由高到低可读可写用户态
内核空间存储操作系统、驱动程序静态+动态由低到高 + 由高到低不能直接访问内核态

二、堆 & 栈

1. 不同变量所在的内存分配

  • 静态存储区(数据段、BSS段):内存分配在程序编译之前完成,且在程序的整个运行期间都存在,例如全局变量、静态变量。
  • :在函数执行时,函数内的局部变量的存储单元在栈上创建,函数执行结束时这些存储单元自动释放局部变量、函数内参数都在栈上。
  • mallocnew开辟的空间在堆上。

2. 堆与栈有什么区别

  • 申请方式
    栈的空间由系统(编译器)自动分配/释放,堆上的空间手动分配/释放。
  • 申请大小的限制
    栈空间有限:栈是向低地址扩展的数据结构,是一块连续的内存,栈的最大容量是系统预先规定好的。
    堆空间较大:堆是向高地址扩展的数据结构,是不连续的内存区域,堆的大小受限于计算机系统中有效的虚拟内存。
  • 申请效率
    栈由系统自动分配,速度较快但程序员是无法控制的。
    堆是由new/malloc分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便。

3. 待补充【栈在 C 语言中有什么作用、C 语言函数参数的压栈顺序、new/delete 与 malloc/free 的区别】

三、内存泄漏

1. 什么是内存泄漏

简单地说就是申请了一块内存空间,使用完毕后没有释放掉。它的一般表现方式是程序运行时间越长,占用内存越多,最终用尽全部内存,导致整个系统崩溃。

2. 如何判断内存泄漏

  1. 良好的编码习惯,尽量在涉及内存的程序段,检测出内存泄露当程式稳定之后,在来检测内存泄露时,无疑增加了排除的困难和复杂度使用了内存分配的函数,一旦使用完毕,要记得要使用其相应的函数释放掉 。
  2. 将分配的内存的指针以链表的形式自行管理,使用完毕之后从链表中删除,程序结束时可检查该链表 。
  3. Boost 中的smart pointer。
  4. 一些常见的工具插件,如ccmalloc、Dmalloc、Leaky等等 。
  • 27
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值