注:以下和体系结构相关的接口都以Mips为例!
创建进程的时候会把父进程的地址空间拷贝一份到子进程,大体的代码调用流程如下:
do_fork->copy_process->dup_mm->dup_mmap->copy_page_range->copy_hugetlb_page_range
从这个流程可以看出,大页进程fork出来的子进程的地址空间也是大页的;
这里有个问题:src_mm的大页vma是如何被创建的?
下面重点分析copy_hugetlb_page_range的流程
整个操作都在for循环里面,分两部分来分析
A:
先用huge_pte_offset取出src中addr对应的pte 表项的地址;这里可能会想不同为什么下面会判断src_pte是否为NULL
很简单,pgd->pud->pmd->pte , pte是第一级页表,也就是最底层的一级页表,如果没有pgd,那么肯定不会有pte了;
所以如果这个addr这个地址在pgd这级都没有建立映射关系,那么根本就可能存在pte后面这一级的地址了;
huge_pte_offset实现如下,Mips的页表映射会在以后详细说明。这里只需要看到的是huge_pte_offset把pmd当作pte返回给调用者,因为这是大页映射;
B:调用huge_pte_alloc为dst分配pte页表项,不明白的是需要判断dst_pte和src_pte是否相等?
这个两个地址都是页表的虚拟地址啊,他俩相等有关系吗?
如果src_pte存在,而且也成功分配了dst_pte,那么就开始做拷贝
如果src_pte的表项中不为空,也就是有正常的映射关系,那么调用huge_ptep_get将src_pte表项中的内容拿出来存放在entry中;
这里需要说明一下,父进程fork出子进程的时候,子进程和父进程实际上还是共享一个物理页面
子进程和父进程的虚拟地址空间也是一样的,但是!子进程需要自己的页表来说明这种映射关系;copy_page_range实际上拷贝的不是物理页面,而是虚拟地址到物理页面的映射关系,也就是页表了;
明白了上面这段话,那几行代码就很容易了;取出src_pte表项中的内容entry,然后将entry写到dst_pte的表项中;
这样的话,父进程和子进程各自的虚拟地址空间实际上对应的是同一个物理页面,通俗点的讲,子进程和父进程共享代码段。。。。
除此之外,还需要增加所src_pte中映射的物理页面的引用计数,
alloc_pages()返回的不是页面的虚拟地址,而是页面的page结构的指针。
内核维护了一个(struct page *)数组,mem_map就是这数组的头,随后的(struct page
*)指针按照(物理)地址顺序进行排列,比如说内存中的第10个页面,它的(struct page
*)指针在mem_map数组中的偏移量(page_nr)就是10,而这个页面的(物理)起始地址就是(10 <<
PAGE_SHIFT) + ARCH_PFN_OFFSET。
总结:对于在ARM上实现hugetlb,需要解决的问题
1:
2:不明白的是,为什么要判断srt_pte和分配的dst_pte值是否相当,这两个值是否相等有关西吗?