堆入门4—堆块申请流程

第一步

  • 程序运行第一次申请堆时,使用malloc函数申请动态内存,glibc会向内核申请一块大内存,这个内存作为之后堆块的来源,这就是 top chunk 。

第二步

  • 再根据用户使用 malloc 申请内存的大小,割让相应大小的内存给 malloc 返回这段内存的起始地址。

第三步

  • 当程序 free 掉内存之后,会将刚才使用到的动态内存返回给glibc,但是返回的内存不是返回给top chunk,而是由bins链管理。

第四步

  • 当程序再次 malloc 时,就从最开始申请到的大内存里进行分配,而不会再向内核申请内存,除非原先申请的内存不够用了,glibc才会再次通过brk/mmap系统调用向内核发起申请。

malloc是应用层的概念,malloc申请的内存实际上是由glibc通过brk和mmap系统调用向内核申请的

上面说的内存就是堆块,堆块就是内存,不要太纠结于这个

堆是由一系列 struct malloc_chunk 结构体组成的链表,申请和释放的堆都是 malloc_chunk 结构体。而内存对于程序的使用上来讲,程序使用这里的内存就是使用 malloc_chunk 结构体。

补充一点点细节

brk 和 mmap

无论是 malloc 函数还是 free 函数,我们动态申请和释放内存时,都经常会使用,但是它们并不是真正与系统交互的函数。这些函数背后的系统调用主要是 (s)brk 函数以及 mmap, munmap 函数。

对于堆的操作,操作系统提供了 brk 函数,glibc 库提供了 sbrk 函数,我们可以通过增加 brk 的大小来向操作系统申请内存。

brk(sbrk)

  • brk通过传递的addr来重新设置 program break,成功则返回0,否则返回-1。而sbrk用来增加heap,增加的大小通过参数increment决定,返回增加大小前的heap的program break,如果increment为0则返回program break。

  • 下图中可以得知,program break 指 heap 的结束地址,start_brk 指 距离 bss 段一节随机距离的地址,也是 heap 开始的地方。

file

heap 从低地址向高地址增长,这张图的文件头在下方。

初始时,堆的起始地址 start_brk 以及堆的末尾地址 program break 指向同一地址。

  • 不开启 ASLR 保护时,start_brk 以及 program break 会指向 data/bss 段的结尾。
  • 开启 ASLR 保护时,start_brk 以及 program break 也会指向同一位置,只是这个位置是在 data/bss 段结尾后的随机偏移处。

mmap

如果malloc一次分配的内存超过了0x20ff8,malloc不再从堆中分配空间,而是使用mmap()这个系统调用从映射区寻找可用的内存空间。

malloc 会使用 mmap来创建独立的匿名映射段。匿名映射的目的主要是可以申请以0填充的内存,并且这块内存仅被调用进程所使用。

void *mmap(void *start,size_t length,int prot,int flags,int fd,off_t offset)
    prot参数指定虚拟内存区域的访问权限:
    PROT_EXEC //这个区域由可以被CPU执行的指令组成
    PROT_READ //可读
    PROT_WRITE //可写
    PROT_NONE //不可访问
    flags参数描述被映射对象类型的位组成:
    MAP_ANON或者MAP_ANONYMOUS //表示被映射的对象是一个匿名对象,相应的虚拟页面是请求二进制零的
    MAP_PRIVATE //对象属性为私有、写时复制的
    MAP_SHARED //表示共享对象

mmap与brk的区别

小于 128 KB 的请求,会在现有的空间中按照堆分配算法(brk、sbrk)为它分配一个堆空间,大于 128 kB 时,就使用 mmap 函数分配一个匿名空间。

详情:https://wiki.x10sec.org/pwn/linux/glibc-heap/heap_overview-zh/#_4

发表评论

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据