DragonOS官网:https://dragonos.org
1. 地址映射管理
1.1. 映射机制设计
对之前的内存管理机制设计的映射部分做了修改:
- 内核空间使用单独的KernelMapper来完成映射过程。并且,对于用户空间的映射,由每个进程专有的UserMapper来负责。区别:内核空间的映射不维护VMA。(将来实现换页机制的时候,内核空间不会被换出)
- KernelMapper是全局的CAS锁,并且允许本地核心双重加锁。(DragonOS已有的SpinLock不允许本地核心双重加锁)
每个进程都有自己的UserMapper实例,用于管理自身的用户地址空间。在用户空间的映射,必须通过VMA来管理。
1.2. 内核映射设计
内核位于高地址空间,0xffff_8000_0000_0000开始的虚拟地址空间。对于所有物理地址,默认将其映射到这一段虚拟地址空间上。(具有线性偏移量)
KernelMapper负责内核空间的映射管理。
KernelMapper内部具有一个PageMapper实例:innerkm。KernelMapper具有一个全局CAS锁,这个CAS具有引用计数功能,允许同一个cpu核心两次进入临界区。(case:原本已经加上锁,然后中断到来,中断上下文内又要加锁)。
1.3. 用户空间映射设计
用户空间的映射管理涉及到的数据结构关系如下:
每个PCB中都有一个地址空间结构体AddressSpace. 这个Address Space里面就包含了mapper、映射信息结构体(UserMappings)以及mmap区域的最低地址。
然后,UserMappings结构里面,具有两个东西:当前进程的所有vma的集合、当前进程的用户地址空间的空洞。这个空洞只的就是,还未被使用的地址空间(注意不是未被映射的)。
2. VMA相关机制设计
2.1. 引入VMA的目的
引入VMA的目的是便于统一管理具有相同属性的一组页面。并且能够支持匿名页的反向映射:根据物理页,查询具有这个物理页的映射的所有页表。
关于匿名页反向映射的必要性,考虑这样一种情况:我们决定要将页面A换出内存,并且,假定当前系统运行1000个进程,其中,页面A出现在了5个进程的页表之中。我们要在这5个进程的页表里面,把页面A置为not-present. 这个“根据物理页,找具有它的映射的所有页表”的过程,就是匿名页反向映射。
如果没有VMA,那么我们必须遍历这1000个进程的所有页表项,才能确定这个物理页出现在了哪些页表的哪些位置。这是很低效的。为此我们才引入VMA,以支持高效的匿名页反向映射。
2.2. VMA的几个数据结构之间的关系
每个匿名页(物理内存页)对应一个anon_vma结构体。这个结构体内维护一个链表,包含指向所有“映射了这个物理页的VMA”。也就是说,一旦把物理页映射到VMA内,那么就要把VMA加入到链表中。
2.3. VMA
每个VMA结构体内部包含的信息如下所示,
包含了:
- 这个VMA管理的地址范围
- 范围内的页面的权限标志位
- 这个VMA是否已经映射
- 如果这个VMA对应的是后备页,存储涉及到的后备页的信息,如文件描述符、偏移量等。
2.4. 当前VMA机制设计的缺陷
当前的设计具有一些缺陷:
- 数据量大时,查询慢:举个例子:当进程不断fork,产生1000个子进程,每个进程有1000个匿名页时,这个anon_vma链表将会变得非常庞大,导致每次查询都会非常慢。将来可通过引入类似Linux的anon_vma_chain的设计来解决。
- anon_vma占用空间大:当前会为每个页面维护独立的anon_vma以及链表,这将会导致很大的内存空间占用。将来可以考虑重用anon_vma,也就是合并相同的anon_vma,以减少内存空间占用。