malloc 初始化_内存管理(23)VMA和malloc

VMA操作

应用进程申请内存空间通常使用malloc或者mmap来完成。我们知道在ARM32架构中0~3G的地址空间是分配给用户进程地址空间的。为了能在系统中(宏观上)同时运行多个进程,互不干扰操作系统想了一个办法,就是基于某一时刻只能运行一个进程的客观事实,系统为每个进程都准备了一套0~3G的虚拟地址空间,他们可以以各自特定的规则映射到物理空间。此刻哪儿个进程在运行就使用哪儿个进程的地址空间及映射关系,再加上写时分配机制的加成。这便有效的解决了进程在宏观上同时运行而绝不会互相干扰的问题。

综上所述,用户进程要申请到内存空间,首先要申请到一块合适的虚拟地址空间供物理地址映射。内核中使用VMA来表示这块虚拟地址空间单元。所以每个进程核心数据结构中都有一个管理VMA的数据结构struct vm_area_struct。

struct vm_area_struct数据结构

6e2dc8ffe49a035c54f8567fdffe1e1f.png

struct vm_area_struct数据结构1

583eb8fcd0156f939527ca4d248cd729.png

struct vm_area_struct数据结构2

666f38bb2a11bdaff0c1e3f4a6295ab5.png

struct vm_area_struct数据结构3

  • vm_start和vm_end:指定VMA进程地址空间的起始结束地址
  • vm_next和vm_prev:VMA连成一个链表
  • vm_rb:VMA作为一个节点加入红黑树,进程的struct mm_struct都有一颗红黑树mm->mm_rb管理这些节点
  • vm:指向该VMA所属进程struct mm_struct
  • vm_page_prot:VMA的访问权限
  • vm_flags:描述VMA的标志
  • anon_vma_chain和anon_vma:用于管理RMAP反向映射
  • vm_ops:指向方法的集合,操作VMA
  • vm_pgoff:指定文件映射的偏移量,这个变量的单位是页。对于匿名页来说它的值是0或者vm_addr/PAGE_SIZE
  • vm_file:指向file的实例,描述一个被映射的文件。

find_vma函数

查找满足如下2个条件之一的VMA:

  1. addr在VMA空间范围内的,vma->vm_start<=addr<=vma->vm_end;
  2. 距离addr最近并且vma->vm_end大于addr的VMA
7c5b278bb3bfb0877e4e4caae8828599.png

find_vma函数

  • 78行:在cache中看能否找到对应地址的vma,能找到直接返回
  • 84~94行:在红黑树中查找合适的VMA
  • 96行:更新cache,最后返回找到的vma

基于find_vma函数实现的还有find_vma_intersection用于查找与start_addr、end_addr重合的VMA;find_vma_prev返回查找到的VMA的前继节点

insert_vm_struct函数

87cd76d92874c10e4717cdae93f77df2.png

insert_vm_struct函数

  • 148行:find_vma_links查找插入位置,实现如下↓
  • 155行:vma_link会调用__vma_link函数把VMA插入红黑树和链表,这个函数很简单不做说明。

find_vma_links函数

5552f06a15dd1fbb2743af1654d489e7.png

find_vma_links函数

  • 165行:__rb_link:指向红黑树的根节点地址
  • 167~180行:在红黑树中查找合适的位置。vma_tmp->vm_end <= addr的不符合寻找条件,继续往右子树探测,如果vma_tmp->vm_end > addr表示符合了基本条件,但是如果vma_tmp->vm_start
  • 最后*rb_link指向待插入位置,*rb_parent表示待插入节点的父节点

malloc函数

19b79d352877119bcf64077b76d1607d.png

用户空间内存空间布局

f08093c30c47f47b362de8a27005c1dd.png

malloc函数-1

5a25717efa3aab0ad88ddf40b3778a86.png

malloc函数-2

  • 278行:基于用户空间的内存空间布局来理解该函数,min_brk表示堆的起始界限也就是数据段的结束位置。brk表示加上申请空间长度后临界线应该处在的位置,因为堆是从底部一直往上增长的,所以如果brk比堆起始位置还小肯定是不合法的。
  • 284~285行:堆的临界线页对齐处理,也就是侧面说明malloc申请的空间也会是页对齐的。
  • 290~294行:堆的新位置比旧位置还小那就是释放空间,调用do_munmap函数来释放空间。
  • 297行:以老边界地址找是否存在重合的vma,如果存在则不需要查找了直接返回。
  • 301行:do_brk函数申请分配内存,大小与页对齐

do_brk函数

302f149de08c2cc432cd77111c8b64df.png

do_brk函数-1

18c6bb1087b0a827942b83f01d340350.png

do_brk函数-2

bbcd80dcee85788773616c15960df521.png

do_brk函数-3

  • 336行:get_unmapped_area判读虚拟地址空间是否有足够的空间,返回一段没有被映射过的虚拟地址空间的起始地址,其中MAP_FIXED表示使用指定具体的虚拟地址对应空间
  • 337行:mlock特性长度检测
  • 347行:判断有没有可能合并附近的VMA,可以的或直接就可以跳转到out,否则只能新建VMA
  • 354~366行:创建VMA,并对齐初始化
  • 367行:将初始化好的VMA添加到红黑树和链表中vma_link这个函数前面介绍过。

回到malloc的系统调用brk函数中

  • 306行:flags是否置位VM_LOCKED,如果置位则调用mm_populate马上分配物理内存并建立映射关系

mm_populate->__mm_populate函数

d1a95d7c5ec497d9a785d9d446f7c79b.png

__mm_populate函数-1

8777e09ad103039a5e8470be15a96e92.png

__mm_populate函数-2

  • 399行:先查找vma,没找到就直接返回
  • 411行:__mlock_vma_pages_range为VMA分配物理内存,实现如下↓

__mlock_vma_pages_range函数

b064a6e0e0396f5391ba94c13c354ed4.png

__mlock_vma_pages_range函数

  • __get_user_pages函数为进程地址空间申请物理内存并建立映射关系

__get_user_pages函数

a07a977f52755bb5ffb9284b021f09c2.png

__get_user_pages函数-1

fd2f3b3ab77a56beb5c439e72e0b41cf.png

__get_user_pages函数-3

b241e80935dcd3f6494f19f2876db76a.png

__get_user_pages函数-4

  • 参数说明:tsk进程核心数据结构;mm进程内存管理核心数据结构;start进程地址空间VMA的起始地址;nr_pages需要分配的pages;gup_flags分配掩码;pages物理页面的二级指针;vmas:进程地址空间VMA;nonblocking:IO操作是否阻塞
  • find_extend_vma会调用find_vma查找VMA。如果vma_start大于start的地址会尝试扩增VMA的范围。如果前期要查找的VMA刚好在gate_vma中就使用gate_vma页面。
  • cond_resched();当前进程是否需要被调度,内核经常在循环中增加这个判断优化系统延迟。
  • follow_page_mask函数查看VMA中的物理页面是否已经分配了内存。函数实现细节如下↓
  • 如果fllow_page_mask分配内存失败,调用faultin_page->handle_mm_fault人为触发缺页中断

follow_page_mask函数

2d02dcd7012f64c63c633192b309cc00.png

follow_page_mask函数-1

cc429bbcba71032719303b6e6b7df277.png

follow_page_mask函数-2

  • 648行:通过mm和address得到当前进程页表对应的PGD目录项
  • 651行:通过PGD和address得到当前进程页表对应的PUD目录项
  • 658行:通过PUD和address得到当前进程页表对应的PMD目录项
  • 670行:调用follow_page_pte检查PTE页面,其实现如下↓

follow_page_pte函数

a9d1de710f56f672a09e90f9b3961838.png

follow_page_pte函数-1

  • 684行:检查pmd合法性
  • 686行:拿到对应的pte页面
  • 688~700行:pte不在内存中的处理。没有定义FOLL_MIGRATION的错误返回;pte_to_swp_entry函数合并交换页面到主内存;migration_entry_wait等待合并交换页面完成,等待完毕重试。
65c19ebdf0353dd99efbe169a2a672c6.png

follow_page_pte函数-2

  • 703行:如果pte是只读页面,flag却含有可写权限,也报错返回。
  • 708行:vm_normal_page函数根据pte返回normal mapping的page数据结构,实现细节如下↓
  • 716~722行:对一些指定标志的page做一些相应的加工处理。对于FOLL_GET的页面会增加page的_count计数;对于FOLL_TOUCH且标记是可写的且非脏的页mark_page_accessed标记为活跃
30308784700130d7ac147dd626a52794.png

follow_page_pte函数-3

  • 724~736行:对VM_LOCKED的page做一些加工处理,最后返回这个page

vm_normal_page函数

a1b06449afdc43836fd9559d3b7915e2.png

vm_normal_page函数-1

  • 755~765行:对于一些特殊映射的页面不返回page数据结构;对于定义了find_special_page函数指针,执行该指针继续检查;对于第0页和特殊映射的页面返回空;
b672fbe62d74f7a39317d0ddc8e4aeac.png

vm_normal_page函数-2

  • 768~783行:对于没有定义与前面类似HAVE_PTE_SPECIAL的情况。775行检测remap_pfn_range的情况。因为其pgoff通常指向第一个pfn;对于写时复制的页面正常返回page;对于0页不返回page结构;

综上:整个malloc的底层处理流程如下:

b8ee434eade7ccb8754e9984e1e991df.png

malloc底层处理流程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值