进程创建的页表初始化

进程创建使用do_fork函数,其中重要的部分如子进程的地址空间的建立,进程空间的建立主要包括线性地址空间和物理地址空间两个方面。其中线性地址空间使用VMA的方式管理,在dup_mmap函数中进行复制和初始化,而物理地址空间使用页目录,页表来管理。

下面分析进程物理地址空间的初始化过程。

整个调用流程:do_fork -> copy_process -> copy_mm -> dup_mm ->

其中进程的页表分为两段,0G-3G用户页表,3G-4G内核页表


内核页表

内核页表的创建 :~/ ->mm_init -> mm_alloc_pgd -> pgd_alloc





pgd_t *pgd_alloc(struct mm_struct *mm)
{
	pgd_t *pgd;
	pmd_t *pmds[PREALLOCATED_PMDS];   
	unsigned long flags;

	pgd = (pgd_t *)__get_free_page(PGALLOC_GFP);   //创建页目录

	if (pgd == NULL)
		goto out;

	mm->pgd = pgd;

	if (preallocate_pmds(pmds) != 0)                  //针对PTE模式下的预分配操作
		goto out_free_pgd;

	if (paravirt_pgd_alloc(mm) != 0)
		goto out_free_pmds;

	/*
	 * Make sure that pre-populating the pmds is atomic with
	 * respect to anything walking the pgd_list, so that they
	 * never see a partially populated pgd.
	 */
	spin_lock_irqsave(&pgd_lock, flags);

	pgd_ctor(pgd);                  //拷贝3G-4G对应的的内核页目录项,因此共享内核页表
	pgd_prepopulate_pmd(mm, pgd, pmds);

	spin_unlock_irqrestore(&pgd_lock, flags);

	return pgd;

out_free_pmds:
	free_pmds(pmds);
out_free_pgd:
	free_page((unsigned long)pgd);
out:
	return NULL;
}



用户页表

用户页表创建:~/ ->dup_mmap ->copy_page_range -> ** -> copy_one_pte

这里的copy_page_range在所有的VMA区域都会执行,根据VMA区域的FLAG做出不同的处理,如SHARE,COW等等。

static inline unsigned long
copy_one_pte(struct mm_struct *dst_mm, struct mm_struct *src_mm,
		pte_t *dst_pte, pte_t *src_pte, struct vm_area_struct *vma,
		unsigned long addr, int *rss)
{
	unsigned long vm_flags = vma->vm_flags;
	pte_t pte = *src_pte;                                  //父进程的页表中分配的page地址
	struct page *page;

	/* pte contains position in swap or file, so copy. */
	if (unlikely(!pte_present(pte))) {
		if (!pte_file(pte)) {
			swp_entry_t entry = pte_to_swp_entry(pte);

			if (swap_duplicate(entry) < 0)
				return entry.val;

			/* make sure dst_mm is on swapoff's mmlist. */
			if (unlikely(list_empty(&dst_mm->mmlist))) {
				spin_lock(&mmlist_lock);
				if (list_empty(&dst_mm->mmlist))
					list_add(&dst_mm->mmlist,
						 &src_mm->mmlist);
				spin_unlock(&mmlist_lock);
			}
			if (likely(!non_swap_entry(entry)))
				rss[MM_SWAPENTS]++;
			else if (is_write_migration_entry(entry) &&
					is_cow_mapping(vm_flags)) {
				/*
				 * COW mappings require pages in both parent
				 * and child to be set to read.
				 */
				make_migration_entry_read(&entry);
				pte = swp_entry_to_pte(entry);
				set_pte_at(src_mm, addr, src_pte, pte);
			}
		}
		goto out_set_pte;
	}

	/*
	 * If it's a COW mapping, write protect it both
	 * in the parent and the child
	 */
	if (is_cow_mapping(vm_flags)) {                      //COW机制,修改页表的标志
		ptep_set_wrprotect(src_mm, addr, src_pte);
		pte = pte_wrprotect(pte);
	}

	/*
	 * If it's a shared mapping, mark it clean in
	 * the child
	 */
	if (vm_flags & VM_SHARED)
		pte = pte_mkclean(pte);
	pte = pte_mkold(pte);

	page = vm_normal_page(vma, addr, pte);            //返回对应的page号

	if (page) {                                       
		get_page(page);                          //增加page的引用次数
		page_dup_rmap(page);
		if (PageAnon(page))                     //区分是匿名映射,还是文件映射,用于系统分析
			rss[MM_ANONPAGES]++;
		else
			rss[MM_FILEPAGES]++;
	}

out_set_pte:
	set_pte_at(dst_mm, addr, dst_pte, pte);             //最后设置页表项
	return 0;
}


以上就是进程的物理地址空间的初始化,写的比较简单,没有针对各个VMA标志进行分析~~





   

你可能感兴趣的:(rss,struct,list,null,DST,migration)