malloc分配内存的原理

malloc内部原理

https://www.xiaolincoding.com/os/3_memory/malloc.html#linux-%E8%BF%9B%E7%A8%8B%E7%9A%84%E5%86%85%E5%AD%98%E5%88%86%E5%B8%83%E9%95%BF%E4%BB%80%E4%B9%88%E6%A0%B7
malloc分配内存原理 视频:https://www.bilibili.com/video/BV1JT421U7rP/?spm_id_from=333.337.search-card.all.click
brk的内部实现 视频:
https://www.bilibili.com/video/BV1xc411q7m9/?spm_id_from=333.337.search-card.all.click
malloc分配的流程:
https://www.bilibili.com/video/BV1RcVhzFEpF/?spm_id_from=333.1387.favlist.content.click&vd_source=0404d052110f319caf00323d54512b50

内存模型



进程的虚拟内存空间:(按逻辑单元划分)
(进程创建时分配虚拟内存空间,并初始化页表(每个进程的页表可以不同))

file
file
代码段,包括二进制可执行代码;
数据段,包括已初始化的静态常量和全局变量;
BSS 段,包括未初始化的静态变量和全局变量;
堆段,包括动态分配的内存,从低地址开始向上增长;
文件映射段,包括动态库、共享内存等,从低地址开始向上增长(跟硬件和内核版本有关 (opens new window));(分为文件映射如mmfile和匿名映射)
栈段,包括局部变量和函数调用的上下文等。栈的大小是固定的,一般是 8 MB。当然系统也提供了参数,以便我们自定义大小;
在这 6 个内存段中,堆和文件映射段的内存是动态分配的。比如说,使用 C 标准库的 malloc() 或者 mmap() ,就可以分别在堆和文件映射段动态分配内存。

PS:
文件映射:
最开始,操作系统仅为其分配虚拟地址空间,但尚未分配实际的物理内存页。
例如:执行 touch file1后,文件在虚拟地址空间中存在,但物理内存未被占用。
物理页按需分配:
首次读写文件时,触发 缺页中断(Page Fault),内核通过 shmem_fault函数分配物理页,并建立虚拟地址到物理页的映射。

匿名映射的使用场景:
file
延迟分配与缺页异常:匿名映射在建立之初,内核通常仅分配虚拟地址空间,物理页的分配会推迟到首次访问​(读或写)并触发缺页异常时进行。
零页(Zero Page)优化:对于首次读取且未写入的私有匿名页,Linux内核会将其映射到一个全局的、全为零的零页,直到发生写操作才分配新的物理页(采用写时复制)。这节省了大量初始化为零的内存开销

段页结合的内存管理

file
如果我们将每个段看做一个单独的程序,则逻辑分段就相当于同时加载多个程序。
file
为了实现地址变换,系统为每个进程建立一张段表,而每个分段有一张页表(在一个进程中,段表只有一个,而页表可能有多个)。段表表项中至少包括段号、页表长度和页表起始地址,页表表项中至少包括页号和块号。此外,系统中还应有一个段表寄存器,指出作业的段表起始地址和段表长度。
在进行地址变换时,首先通过段表查到页表起始地址,然后通过页表找到页帧号,最后形成物理地址。如图所示,进行一次访问实际需要三次访问主存,这里同样可以使用快表以加快查找速度,其关键字由段号、页号组成,值是对应的页帧号和保护码。
file
TLB
file

内存换入换出

malloc() 分配的是虚拟内存。
如果分配后的虚拟内存没有被访问的话,虚拟内存是不会映射到物理内存的,这样就不会占用物理内存了。
只有在访问已分配的虚拟地址空间的时候,操作系统通过查找页表,发现虚拟内存对应的页没有在物理内存中,就会触发缺页中断,然后操作系统会建立虚拟内存和物理内存之间的映射关系。
换入:
需要注意的是,并非所有的缺页中断都涉及Swap机制。例如,当程序第一次访问一个已分配但尚未加载的代码段或数据文件时(即按需调页),也会发生缺页中断,此时数据是从原始程序文件或库文件中读入内存,而不是从Swap空间

换出后是存在硬盘的swap分区:
https://juejin.cn/post/7113143881004220452
https://www.sidney.wiki/system/1304

malloc分配内存的流程:

file
1.遍历fast_bins
2.遍历unsorted_list,查找合适size的chunk,如果找到则返回;否则,将这些chunk都归类放到smallbins和largebins里面
3.如果分配内存<512字节,则通过内存大小定位到smallbins对应的index上(floor(size/8))
如果smallbins[index]为空,进入步骤5
如果smallbins[index]非空,直接返回第一个chunk
4.如果分配内存>512字节,则定位到largebins对应的index上
如果largebins[index]为空,进入步骤5
如果largebins[index]非空,扫描链表,找到第一个大小最合适的chunk,如size=12.5K,则使用chunk B,剩下的0.5k放入unsorted_list中
5.index++从更大的链表中查找,直到找到合适大小的chunk为止,找到后将chunk拆分,并将剩余的加入到unsorted_list中
7.不足时切割top chunk
6.top chunk还不足,内存<128k,使用brk;内存>128k,使用mmap获取新内存

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

Contents
滚动至顶部