2024年3月21日发(作者:)
第2章 内存管理
31 if (addr) {
32 if (do_align)
33 addr = COLOUR_ALIGN(addr, pgoff);
34 else
35 addr = PAGE_ALIGN(addr);
36 vma = find_vma(mm, addr);
37 if (TASK_SIZE - len >= addr &&
38 (!vma || addr + len <= vma->vm_start))
39 return addr;
40 }
41
42 ...
43}
arch_get_unmapped_area_topdown()是ARM架构里get_unmapped_area()函数的实现,
该函数留给读者自行阅读。
第20行代码中的find_vma_links()函数之前已经阅读过,它循环遍历用户进程红黑树中
的VMAs,然后根据addr来查找最合适插入到红黑树的节点,最终rb_link指针指向最合适
节点的rb_left或rb_right指针本身的地址。返回0表示寻找到最合适插入的节点,返回
-ENOMEM表示和现有的VMA重叠,这时会调用do_munmap()函数来释放这段重叠的空间。
do_brk()函数中的第37行,vma_merge()函数去找有没有可能合并addr附近的VMA。
如果没办法合并,那么只能新创建一个VMA,VMA的地址空间就是[addr, addr+len]。
第53行代码,新创建的VMA需要加入到mm->mmap链表和红黑树中,vma_link()函
数实现这个功能,该函数之前已经阅读过。
回到do_brk函数中,新创建了VMA、完成插入并且更新一些变量之后,返回这个VMA
的起始地址。
回到brk函数中,第39行代码,这里判断flags是否置位VM_LOCKED,这个
VM_LOCKED通常从mlockall系统调用中设置而来。如果有,那么需要调用mm_populate()
马上分配物理内存并建立映射。通常用户程序很少使用VM_LOCKED分配掩码,所以brk
不会为这个用户进程立马分配物理页面,而是一直将分配物理页面的工作推延到用户进程需
要访问这些虚拟页面时,发生了缺页中断才会分配物理内存,并和虚拟地址建立映射关系。
2.8.2 VM_LOCK情况
当指定VM_LOCK标志位时,表示需要马上为这块进程地址空间VMA的分配物理页
面并建立映射关系。mm_populate()函数内部调用__mm_populate(),参数start是VMA的起
始地址,len是VMA的长度,ignore_errors表示当分配页面发生错误时会继续重试。
[brk系统调用->mm_populate()->__mm_populate()]
0 int __mm_populate(unsigned long start, unsigned long len, int ignore_errors)
1 {
2 struct mm_struct
*
mm = current->mm;
3 unsigned long end, nstart, nend;
4 struct vm_area_struct
*
vma = NULL;
138
2.8 malloc
5 int locked = 0;
6 long ret = 0;
7
8 VM_BUG_ON(start & ~PAGE_MASK);
9 VM_BUG_ON(len != PAGE_ALIGN(len));
10 end = start + len;
11
12 for (nstart = start; nstart < end; nstart = nend) {
13 /
*
14
*
We want to fault in pages for [nstart; end) address range.
15
*
Find first corresponding VMA.
16
*
/
17 if (!locked) {
18 locked = 1;
19 down_read(&mm->mmap_sem);
20 vma = find_vma(mm, nstart);
21 } else if (nstart >= vma->vm_end)
22 vma = vma->vm_next;
23 if (!vma || vma->vm_start >= end)
24 break;
25 /
*
26
*
Set [nstart; nend) to intersection of desired address
27
*
range with the first VMA. Also, skip undesirable VMA types.
28
*
/
29 nend = min(end, vma->vm_end);
30 if (vma->vm_flags & (VM_IO | VM_PFNMAP))
31 continue;
32 if (nstart < vma->vm_start)
33 nstart = vma->vm_start;
34 /
*
35
*
Now fault in a range of pages. __mlock_vma_pages_range()
36
*
double checks the vma flags, so that it won't mlock pages
37
*
if the vma was already munlocked.
38
*
/
39 ret = __mlock_vma_pages_range(vma, nstart, nend, &locked);
40 nend = nstart + ret
*
PAGE_SIZE;
41 ret = 0;
42 }
43 if (locked)
44 up_read(&mm->mmap_sem);
45 return ret; /
*
0 or negative error code
*
/
46}
第12行代码,以start为起始地址,先通过find_vma()查找VMA,如果没找到VMA,
则退出循环。
第39行代码调用__mlock_vma_pages_range()函数为VMA分配物理内存。
[__mm_populate()->__mlock_vma_pages_range()]
0 long __mlock_vma_pages_range(struct vm_area_struct
*
vma,
1 unsigned long start, unsigned long end, int
*
nonblocking)
2 {
3 struct mm_struct
*
mm = vma->vm_mm;
4 unsigned long nr_pages = (end - start) / PAGE_SIZE;
5 int gup_flags;
6
7 VM_BUG_ON(start & ~PAGE_MASK);
8 VM_BUG_ON(end & ~PAGE_MASK);
139
发布评论