在glibc中是如何分配一块堆内存的(malloc)

一、内存是如何产生的

libc(malloc/free)–>系统调用brk()或者mmap–>返回内存–>读写内存–>缺页中断–>物理与虚拟的内存映射

  1. 在C语言的libc库中封装着malloc和free等跟内存处理相关的函数,而libc中有封装着系统调用brk(),当使用malloc和free时,就会调用brk(),这就是为什么分配内存就会从用户态转换到内核态

  2. (1)当使用malloc时,会将堆增大,在内核中,会确定程序的heap段的起始地址min_brk,然后在从增加的内存大小重新确定heap段的结束地址(这就是为什么程序中堆地址是向上增长的)。但是这中间涉及到一个小内存和大内存的分配方式。

    (2)在linux中,有一个门槛值,申请小于这个门槛值的内存称为小内存,大于这个门槛值的内存称为大内存。对于大内存和小内存而言,操作系统的分配操作分别使用的是mmap和brk()。这些内存都是分配在进程的堆空间中的。

  3. 那么这时候就可以开始讲讲操作系统是如何给程序来分配堆空间的内存了。首先先介绍小内存的分配方式。首先每malloc的一段k值的小内存都会使得_end_data这个指针(指向所有小内存的尾部)增加k值,然后如果在中间释放了某一块小内存,那么这一块内存会可以再次被分配出去,当遇到符合分配大小的小内存时,系统会将这块内存再次分配出去。但是如果使用多次malloc和free小内存会产生这些小的内存间隔,可能不会被利用到,这就是内存碎片的产生,只有到了_end_data这指向的内存被释放了,才会将与_end_data连续的堆内存进行整合成为一块大段的内存,才可以减少一部分的内存碎片。在太多的内存碎片的情况下,会导致_end_data越来越大,直到占满整个用户空间的堆空间。

  4. 对于大内存,使用了mmap来进行分配,当使用了mmap的情况下,会在_end_data和栈底(栈空间的地址是向下生长)的这部分空间中进行分配。这一部分内存可以被直接回收,不会产生内存碎片。

  5. 什么时候开始物理内存与虚拟内存的对应。只有当程序开始往这块内存读写数据的时候,才会触发缺页中断,然后操作系统将这块虚拟内存映射到物理内存中。

  6. 拓展说一下linux是如何管理物理内存的,1、物理页而言,使用伙伴系统(以页为单位),2、对于小字节(不以页的大小为单位)而言,使用slab分配器。

二、为什么在linux上读取到空指针会导致程序crash

  1. linux会为进程的用户空间的0x0的虚拟地址映射到一个用户没有权限访问的页中,内核保证没有其它页映射到这个区域。
  2. 编译器会将空指针编译成指向0x0的虚拟地址,程序可以去读取地址
  3. 这时候会按照正常流程,读取地址就会触发缺页中断,缺页异常程序会被调用,然后去读取一块没有访问权限的页,缺页异常程序发现没有访问权限,内核会发送SIGSEGV 信号给进程,该信号默认是让进程自杀。

三、malloc的一些设计理念

以下结论是从malloc的源码得来。

  1. malloc的设计目的是为了最节省内存空间,而不是为了速度最快而设计的。
    • malloc处理大于512bytes,小于128KB的内存分配请求时性能回更加优越。
    • 处理小于64bytes的内存请求时,使用池化技术进行维护。
    • 大于128KB的内存请求时,使用系统提供的mmap来进行内存分配。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值