1688网站,18款禁用软件app网站入口,网站新闻页面设计,ftp网站服务器参考#xff1a;Linux笔记–堆简介 地址#xff1a;https://qingmu.blog.csdn.net/article/details/119510863 目录1、前言2、堆的由来3、Linux中堆简介4、堆分类4.1、请求堆4.2、释放堆5、内存分配背后的系统调用6、堆相关数据结构7、堆的申请8、调试验证1、前言
当前针对各… 参考Linux笔记–堆简介 地址https://qingmu.blog.csdn.net/article/details/119510863 目录1、前言2、堆的由来3、Linux中堆简介4、堆分类4.1、请求堆4.2、释放堆5、内存分配背后的系统调用6、堆相关数据结构7、堆的申请8、调试验证1、前言
当前针对各大平台主要有如下几种堆内存管理机制
平台堆管理机制dlmallocGeneral purpose allocatorptmalloc2glibcjemallocFreeBSD and FirefoxtcmallocGooglelibumemSolaris
在本文中我们主要说的是Linux中glibc的堆管理机制。
2、堆的由来 Linux中早期的堆分配与回收由Doug Lea 实现。 但是它在并行处理多线程的时候会共享进程的堆内存空间。因此为了安全性一个线程使用堆时会进行加锁。 然而与此同时加锁会导致其他线程无法使用堆降低了内存分配和回收的高效性。同时如果多线程使用时没能正确控制也有可能引起内存分配和回收的正确性。 Wolfram Gloger 在Doug Lea 的基础上进行改进使其支持多线程这个堆分配器就是ptmalloc。在glibc-2.3.x.之后glibc中集成了ptmalloc2。
1、只有当真正访问一个地址的时候系统才会建立虚拟页面与物理页面的映射关系。2、所以虽然操作系统已经给程序分配了很大的一块内存
但是这块内存其实只是虚拟内存。只有当用户使用到相应的内存时
系统才会真正分配物理内存给用户使用。3、Linux中堆简介 目前Linux标准发行版中使用的堆分配器是glibc中的堆分配器ptmalloc2。ptmalloc2主要是通过malloc/free函数来分配和释放内存块。 需要注意的是 在内存的分配与使用过程中Linux有这样的一个基本内存管理思想
4、堆分类
4.1、请求堆
相应用户的申请内存请求向操作系统申请内存然后将其返回给用户程序。同时为了保持内存管理的高效性内存一般都会预先分配一块很大的连续的内存(这块很大的内存称之为top_chunk)然后让堆管理器通过某种算法管理这块内存。只有当出现堆空间不足的情况堆管理器才会再次与操作系统交互。
通俗的说就是我们的malloc或者new等申请内存的操作函数。
4.2、释放堆
管理用户所释放的内存。一般来说用户释放的内存并不是直接返还给操作系统的而是由堆管理器进行管理。这些释放的内存可以来响应用户新申请的内存的请求。 通俗的说就是我们的free或者delete等释放内存的操作函数。
5、内存分配背后的系统调用 我们在申请与释放内存的函数中无论是malloc还是free函数一般都会经常的使用但是他们并不是真正与系统交互的函数。 这些函数背后的系统调用主要是(s)brk函数以及mmapunmmap函数。
具体流程如下图所示 对于堆的操作操作系统提供了brk函数我们可以通过增加brk的大小来向操作系统申请内存。 初始时堆得起始地址start_brk以及堆的当前末尾brk指向的地址。根据是否开启ALSR地址随机化保护时两者的具体位置会有所不同。 不开启ALSR保护时start_brk以及brk会指向data/bss段的结尾。 开启ALSR保护时start_brk以及brk也会指向同一位置只是这个位置是在data/bss段的结尾后的随机偏移处。
可参考下图
6、堆相关数据结构
堆的操作是十分的复杂的那么在glibc内部必然也有精心设计的数据结构链表来管理它与堆相应的数据结构主要分为
宏观结构1、包含堆的宏观信息可以通过这些数据结构索引堆的基本信息2、主要是堆块之间的链接微观结构1、用于处理堆分配与回收的内存块2、主要还是怎么处理堆的分配与回收中的内存块3、malloc free7、堆的申请 在程序的执行中我们由malloc申请的内存称之为chunk。这块内存在ptmalloc内部用malloc_chunk 结构体来表示。当程序申请的chunk被free后会被加入到空闲管理列表中这个列表被称之为binlist。 无论一个chunk的大小如何处于分配状态还是释放状态他们都使用一个统一的结构体。 虽然他们都使用同一个数据结构但是根据是否被释放他们的表现形式会不一样。
malloc_chunk 的具体形式如下
struct malloc_chunk
{INTERNAL_SIZE_T prev_size;INTERNAL_SIZE_T size;struct malloc_chunk *fd;struct malloc_chunk *bk;struct malloc_chunk *fd_nextsize;struct malloc_chunk *bk_nextsize;
}我们由malloc申请一块0x100的内存的时候这块内存在ptmalloc内部用malloc_chunk来表示但是他的大小并不是0x100的大小还得加上我们操作不了的prev_size和size大小一般为0x10。
参数详解
size
1、他是一个标志位2、该字段的低三个比特对chunk的大小没有影响他们从低到高分别表示NON_MAIN_ARENA记录当前chunk是否不属于主线程1表示不属于0表示属于。3、IS_MAPPED ,记录当前chunk是否有mmap分配的。4、PREV_INUSE记录前一个chunk块是否被分配。一般来说堆中的第一个被分配的内存块的size字段的P位都会被置位1以便于防止访问前面的非法内存。当一个chunk的size的P位为0时我们能通过pre_size字段来获取上一个chunk的大小以及地址。这也方便进行空闲chunk之间的合并。A|M|Pfdbk
1、chunk处于分配状态时从fd字段开始是用户数据。chunk空闲时会被添加到空闲的管理列表中binlist其字段的含义是指向下一个非物理相邻空闲的chunk。2、bk指向上一个非物理相邻空闲chunk3、通过fd和bk可以将空闲的chunk块加入到空闲的chunk块链表进行统一管理
fd_nextsize bk_nextsize
1、也是同样只有chunk空闲的时候才可以使用不过其用于较大的chunklarge chunk2、fd_nextsize指向前一个与当前chunk大小不用的第一个空闲块不包含bin的头指针3、bk_nextsize指向后一个与当前chunk大小不用的第一个空闲块包含bin的头指针4、一般空闲的large chunk 在fd的遍历顺序中按照由大到小顺序排列。这样可以避免在寻找合适chunk时挨个遍历8、调试验证
我们使用gdb调试验证一下
调试的C代码如下
#includestdio.h
#includemalloc.h
#includeunistd.h
#includestring.h
int main(){int size 0x100;void *p malloc(size);void *junk malloc(size);void *q malloc(size);void *r malloc(size);printf(p:0x%x\n,p);printf(q:0x%x\n,q);printf(r:0x%x\n,r);strcpy(p,aaaaaaaabbbbbbbb);strcpy(q,ccccccccdddddddd);strcpy(r,eeeeeeeeffffffff);sleep(0);free(p);sleep(0);free(q);sleep(0);
// q malloc(0x600);
// sleep(0);return 0;
}可以看到在程序起来后并没有堆的任何信息本来我们也没申请嘛咱们接着往下调
让其走完一个malloc走完看一看 这里我们申请的是0x100的大小真是大小却是2730x111因为加上了我们不可操作的prev_size和size的大小。
接着往下看走完printf 我们发现printf也会创建堆块为了存放标准输入标准输出的咱们接着往下走到给p,q,r赋值之后 堆中的fd和bk都赋值为了我们赋值的东西。
再接着我们free掉p再看一下堆的情况 他的fd和bk已经被改变了其值为libc中的地址我们在把q给free掉 我们会发现q的bk已经指向了q这个堆块的地址了而q这个堆的fd就变成了p这个堆的地址了他们被ptmalloc组成了链表形成了bin链。