- 论坛徽章:
- 0
|
本帖最后由 cluter 于 2011-06-05 15:32 编辑
只讲大体流程,不过分追究细节了,因为细节我也没全搞懂,所以只能先整体上把握了。。。 希望大牛指正!- //******************************copy_mm**********************************************
- static int copy_mm(unsigned long clone_flags, struct task_struct * tsk)
- {
- // 1 如果内存空间共享,直接增加内存描述符mm的引用计数,然后退出
- if (clone_flags & CLONE_VM) {
- atomic_inc(&oldmm->mm_users);
- mm = oldmm;
- goto good_mm;
- }
- // 2 如果内存空间不共享,则创建一个新的内存空间
- mm = dup_mm(tsk);
- good_mm:
- // 3 对于进程来说mm和active_mm指向同一内存描述符mm
- // 对于内核线程来说,使用active_mm指向当前进程的内存描述符mm
- // 内核线程不使用线性区
- tsk->mm = mm;
- tsk->active_mm = mm;
- return 0;
- }
- struct mm_struct *dup_mm(struct task_struct *tsk)
- {
- // 1 为新进程分配一个新的内存描述符mm
- mm = allocate_mm();
- // 2 先拷贝父进程内存描述符的所有内容
- memcpy(mm, oldmm, sizeof(*mm));
- // 3 初始化新内存描述符mm中的字段(包括为新进程分配一个新的pgd)
- if (!mm_init(mm, tsk))
- goto fail_nomem;
- // 4 复制父进程的线性区间和页表
- err = dup_mmap(mm, oldmm);
- }
- // 1 分配新的vma线性区并初始化
- // 2 如果vma线性区是file 映射则加入priority-search tree
- // 3 把新的vma线性区加入内存描述符mm的red-black tree
- // 4 链接vma线性区间
- // 5 复制页表项
- static int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm)
- {
- struct vm_area_struct *mpnt, *tmp, *prev, **pprev;
- struct rb_node **rb_link, *rb_parent;
- int retval;
- unsigned long charge;
- struct mempolicy *pol;
- //获取锁
- down_write(&oldmm->mmap_sem);
- flush_cache_dup_mm(oldmm);
- /*
- * Not linked in yet - no deadlock potential:
- */
- down_write_nested(&mm->mmap_sem, SINGLE_DEPTH_NESTING);
- //初始化mm描述符
- mm->locked_vm = 0;
- mm->mmap = NULL;
- mm->mmap_cache = NULL;
- mm->free_area_cache = oldmm->mmap_base;
- mm->cached_hole_size = ~0UL;
- mm->map_count = 0;
- cpumask_clear(mm_cpumask(mm));
- mm->mm_rb = RB_ROOT;
- rb_link = &mm->mm_rb.rb_node;
- rb_parent = NULL;
- pprev = &mm->mmap;
- retval = ksm_fork(mm, oldmm);
- if (retval)
- goto out;
- prev = NULL;
- //开始复制vma线性区
- for (mpnt = oldmm->mmap; mpnt; mpnt = mpnt->vm_next) {
- struct file *file;
- //线性区间不允许拷贝
- if (mpnt->vm_flags & VM_DONTCOPY) {
- long pages = vma_pages(mpnt);
- //那么进程地址空间大小就要减去该线性区间
- mm->total_vm -= pages;
- vm_stat_account(mm, mpnt->vm_flags, mpnt->vm_file,
- -pages);
- continue;
- }
- charge = 0;
- //IPC共享线性区,检查是否有足够的内存用于映射
- if (mpnt->vm_flags & VM_ACCOUNT) {
- unsigned int len = (mpnt->vm_end - mpnt->vm_start) >> PAGE_SHIFT;
- if (security_vm_enough_memory(len))
- goto fail_nomem;
- charge = len;
- }
- //分配一个新的vma线性区
- tmp = kmem_cache_alloc(vm_area_cachep, GFP_KERNEL);
- if (!tmp)
- goto fail_nomem;
- //复制内容
- *tmp = *mpnt;
- INIT_LIST_HEAD(&tmp->anon_vma_chain);
- pol = mpol_dup(vma_policy(mpnt));
- retval = PTR_ERR(pol);
- if (IS_ERR(pol))
- goto fail_nomem_policy;
- vma_set_policy(tmp, pol);
- if (anon_vma_fork(tmp, mpnt))
- goto fail_nomem_anon_vma_fork;
- tmp->vm_flags &= ~VM_LOCKED;
- tmp->vm_mm = mm;
- tmp->vm_next = tmp->vm_prev = NULL;
- file = tmp->vm_file;
- //该vma线性区是否是文件映射
- if (file) {
- //获取该文件映射对应的address_space
- struct inode *inode = file->f_path.dentry->d_inode;
- struct address_space *mapping = file->f_mapping;
- get_file(file);
- if (tmp->vm_flags & VM_DENYWRITE)
- atomic_dec(&inode->i_writecount);
- spin_lock(&mapping->i_mmap_lock);
- if (tmp->vm_flags & VM_SHARED)
- mapping->i_mmap_writable++;
- tmp->vm_truncate_count = mpnt->vm_truncate_count;
- flush_dcache_mmap_lock(mapping);
- /* insert tmp into the share list, just after mpnt */
- //新的vma线性区直接插入priority-search tree
- //priority-search tree主要用于页框回收时,释放所有映射该页框的页表项
- vma_prio_tree_add(tmp, mpnt);
- flush_dcache_mmap_unlock(mapping);
- spin_unlock(&mapping->i_mmap_lock);
- }
- /*
- * Clear hugetlb-related page reserves for children. This only
- * affects MAP_PRIVATE mappings. Faults generated by the child
- * are not guaranteed to succeed, even if read-only
- */
- if (is_vm_hugetlb_page(tmp))
- reset_vma_resv_huge_pages(tmp);
- /*
- * Link in the new vma and copy the page table entries.
- */
- //把刚分配的vma线性区链接起来
- *pprev = tmp;
- pprev = &tmp->vm_next;
- tmp->vm_prev = prev;
- prev = tmp;
- //把新分配的vma线性区插入red-black tree
- __vma_link_rb(mm, tmp, rb_link, rb_parent);
- rb_link = &tmp->vm_rb.rb_right;
- rb_parent = &tmp->vm_rb;
- mm->map_count++;
- //拷贝页表项
- retval = copy_page_range(mm, oldmm, mpnt);
- if (tmp->vm_ops && tmp->vm_ops->open)
- tmp->vm_ops->open(tmp);
- if (retval)
- goto out;
- }
- /* a new mm has just been created */
- arch_dup_mmap(oldmm, mm);
- retval = 0;
- out:
- up_write(&mm->mmap_sem);
- flush_tlb_mm(oldmm);
- up_write(&oldmm->mmap_sem);
- return retval;
- fail_nomem_anon_vma_fork:
- mpol_put(pol);
- fail_nomem_policy:
- kmem_cache_free(vm_area_cachep, tmp);
- fail_nomem:
- retval = -ENOMEM;
- vm_unacct_memory(charge);
- goto out;
- }
- //************copy_page_range**********************
- //调用流程
- copy_page_range
- |
- |
- |
- copy_pud_range
- |
- |
- |
- copy_pmd_range
- |
- |
- |
- copy_pte_range
- |
- |
- |
- copy_one_pte
- //对于copy_one_pte有两个重要的地方
- // 最开始是关于如果页表所映射的页框已经在swap中的处理过程
- // 1 如果是copy on write映射,则父子页表都要写保护
- /*
- * If it's a COW mapping, write protect it both
- * in the parent and the child
- */
- if (is_cow_mapping(vm_flags)) {
- ptep_set_wrprotect(src_mm, addr, src_pte);
- pte = pte_wrprotect(pte);
- }
- // 2 如果是共享映射 ,则清空子进程页表项,属于请求调页
- [size=7][color=Magenta] //如果是共享映射,为啥不直接负责页表项,而是清空子进程页表项?[/color][/size]
- /*
- * 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);[color=Red][size=7]------>这里标记页表被访问过是啥意思?[/size][/color]
复制代码 |
评分
-
查看全部评分
|