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