之前讲过一篇用户态程序在linux当中的布局,今天来讲讲linux内核是怎么管理这个布局的。

由于用户的虚拟地址空间比较大(在32位系统中通常是3G),所以内核采取了一种称之为按需调页法,其步骤如下:
1. 进程试图访问用户地址空间中的一个内存地址,但是页表中没有该项
2. 处理器触发一个缺页异常,发送到内核
3. 内核会检查负责缺页区域的进程地址空间的数据结构,找到适当的底层存储,或者确认访问实际上是不正确的
4. 分配物理内存页,并从底层存储读取数据填充之
5. 借助于页表将物理内存页并入到用户进程地址空间,应用程序继续运行。

在task_struct中,有一个struct mm_struct 负责表示整个进程在内存布局当中的所有必要信息,另外,它还包括下列成员,用于管理用户进程在虚拟地址空间中的所有内存区域

点击(此处)折叠或打开

  1. struct mm_struct{
  2.     struct vm_area_struct *mmap;/*虚拟内存区域列表*/
  3.     struct rb_root mm_rb;
  4. ...
  5. };

点击(此处)折叠或打开

  1. struct vm_area_struct {
  2.     /* The first cache line has the info for VMA tree walking. */

  3.     unsigned long vm_start;        /* Our start address within vm_mm. */
  4.     unsigned long vm_end;        /* The first byte after our end address
  5.                      within vm_mm. */

  6.     /* linked list of VM areas per task, sorted by address */
  7.     struct vm_area_struct *vm_next, *vm_prev;

  8.     struct rb_node vm_rb;

  9.     /*
  10.      * Largest free memory gap in bytes to the left of this VMA.
  11.      * Either between this VMA and vma->vm_prev, or between one of the
  12.      * VMAs below us in the VMA rbtree and its ->vm_prev. This helps
  13.      * get_unmapped_area find a free area of the right size.
  14.      */
  15.     unsigned long rb_subtree_gap;

  16.     /* Second cache line starts here. */

  17.     struct mm_struct *vm_mm;    /* The address space we belong to. */

  18.     /*
  19.      * Access permissions of this VMA.
  20.      * See vmf_insert_mixed_prot() for discussion.
  21.      */
  22.     pgprot_t vm_page_prot;
  23.     unsigned long vm_flags;        /* Flags, see mm.h. */

  24.     /*
  25.      * For areas with an address space and backing store,
  26.      * linkage into the address_space->i_mmap interval tree.
  27.      */
  28.     struct {
  29.         struct rb_node rb;
  30.         unsigned long rb_subtree_last;
  31.     } shared;

  32.     /*
  33.      * A file's MAP_PRIVATE vma can be in both i_mmap tree and anon_vma
  34.      * list, after a COW of one of the file pages.    A MAP_SHARED vma
  35.      * can only be in the i_mmap tree. An anonymous MAP_PRIVATE, stack
  36.      * or brk vma (with NULL file) can only be in an anon_vma list.
  37.      */
  38.     struct list_head anon_vma_chain; /* Serialized by mmap_sem &
  39.                      * page_table_lock */
  40.     struct anon_vma *anon_vma;    /* Serialized by page_table_lock */

  41.     /* Function pointers to deal with this struct. */
  42.     const struct vm_operations_struct *vm_ops;

  43.     /* Information about our backing store: */
  44.     unsigned long vm_pgoff;        /* Offset (within vm_file) in PAGE_SIZE
  45.                      units */
  46.     struct file * vm_file;        /* File we map to (can be NULL). */
  47.     void * vm_private_data;        /* was vm_pte (shared mem) */
  48. ...
  49. } __randomize_layout;



每个内存区域通过一个vm_area_struct 来描述,进程的各个区域按照两种方法排序:
a)在一个链表上,即mmap,现存的区域按照其实地址递增顺序放入链表当中
b)在一个红黑树上,根节点即mm_rb
这样的话,当新添加一个区域的时候,就会进行如下判断,并且插入到红黑树和链表当中
1. 如果一个新区域可以合并到原有的区域,或者将两个原有的区域合并,则合并
2. 如果删除的区域在一个区域的末尾,直接缩减
3. 如果删除的区域在一个区域的中间,分裂成两个新的区域


总体来看,就相当于在一堆排好序的没有重叠的区间当中添加和删除一个区域时需要进行的常规操作


10-10 18:12