LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
查看: 902|回复: 6

请问初始化新进程的函数在哪里?

[复制链接]
发表于 2004-9-27 13:14:16 | 显示全部楼层 |阅读模式
大家好,请问初始化新进程的函数在哪里?Linux 中的 do_fork() 似乎和 UNIX 中的 fork() 不太一样。
发表于 2004-9-30 00:30:53 | 显示全部楼层
Linux  综合使用了BSD的vfork()和SystemV的fork()COW技术,


fork和vfork都是调用同一个核心函数do_fork。
 楼主| 发表于 2004-9-30 12:49:32 | 显示全部楼层
UNIX V6 的 fork() 函数调用了 newproc() 来初始化页表,我想读读 Linux 中的对应代码,可是没有找到。虽然 Linux 中使用了动态的结构,但原理应该一样吧,它在哪里呢?谢谢。
发表于 2004-10-8 00:06:33 | 显示全部楼层
很久没讨论了。。
这段时间看了一下linux内核方面内容,写一点,不对的地方请各位指正。

重点讨论linux fork()系统调用虚拟地址空间的重建过程:


[PHP]新进程是通过克隆旧进程或说克隆当前进程而创建。新任务通过一个系统调用( f o r k或
c l o n e )而创建,克隆过程发生在核心模式下的内核中,在系统调用的末尾会有一个新进程等待
调度器选中它并运行。一个新的t a s k _ s t r u c t数据结构被从系统物理内存分配,以及一页或多页
物理内存作为克隆的进程的栈(用户的和核心的)。一个进程标识符可能会被创建:一个在系统
进程标识符集合中唯一的标识符。然而,克隆的进程完全有理由保存其父进程的标识符。新
的t a s k _ s t r u c t新加入到t a s k向量并且老的(当前)进程的t a s k _ s t r u c t的内容被复制到克隆出的
t a s k _ s t r u c t中。
当克隆进程时, L i n u x允许两个进程共享资源而不是有两份独立的副本。这种共享可用于
进程的文件、信号处理器和虚拟内存。当资源被共享时,它们的c o u n t字段将被递增,以便在
两个进程都结束访问它们之前Linux 不会回收这些资源。举例来说,如果克隆的进程将共享虚
拟内存,其t a s k _ s t r u c t将包含指向原先进程的m m _ s t r u c t的指针并且该m m _ s t r u c t将把其c o u n t
字段递增以表明当前共享该页的进程数。
克隆一个进程的虚拟内存是很有技巧性的。一个新的v m _ a r e a _ s t r u c t集合以及它们拥有的
m m _ s t r u c t数据结构,还有被克隆的进程的页表必须被产生出来。在这时没有进程的任何虚拟
内存被复制。复制将是一件很困难和冗长的工作,因为一些虚拟内存在物理内存,一些在进
程正在执行的可执行映像中,还可能有一些在交换文件中;相反, L i n u x使用称为“写时复制
(copy on write)”的技巧,这意味着仅当两个进程试图写它时虚拟内存才会被复制。任何虚拟
内存只要没被写,即使它可以被写,也将在两个进程间共享而不会引起任何危害。只读的内
存(比如可执行代码)总是被共享。为了使“写时复制”工作,可写区域将其页表项标识为只读
并且v m _ a r e a _ s t r u c t数据结构描述为它们被标识成”写时复制”。当一个进程试图写该虚拟内
存时将产生一个页故障。正是在此时L i n u x将产生该内存的一个副本,并修改两个进程的页表
和虚拟内存数据结构。[/PHP]

上面这些摘自《Linux编程白皮书》中描述的。
原理也就这样。
发表于 2004-10-8 00:44:50 | 显示全部楼层
fork系统调用内部调用copy_mm()实现虚拟地址空间的复制。
\linux-2.6.8\kernel\fork.c

  1. static int copy_mm(unsigned long clone_flags, struct task_struct * tsk)
  2. {
  3.         struct mm_struct * mm, *oldmm;
  4.         int retval;

  5.         tsk->min_flt = tsk->maj_flt = 0;
  6.         tsk->cmin_flt = tsk->cmaj_flt = 0;
  7.         tsk->nvcsw = tsk->nivcsw = tsk->cnvcsw = tsk->cnivcsw = 0;

  8.         tsk->mm = NULL;
  9.         tsk->active_mm = NULL;
  10.         /* [color=blue] 初始化tsk中(子进程)
  11. 一些mm相关域*/[/color]

  12.         /*
  13.          * Are we cloning a kernel thread?
  14.          *
  15.          * We need to steal a active VM for that..
  16.          */
  17.         oldmm = current->mm;
  18.         if (!oldmm)
  19.                 return 0;

  20.         if (clone_flags & CLONE_VM) {
  21.                 atomic_inc(&oldmm->mm_users);
  22.                 mm = oldmm;/*[color=blue] 如由vfork()调用,
  23. 直接将父进程mm_struct结构(oldmm = current->mm)返回给子进程,父子共享!*/[/color]
  24.                 /*
  25.                  * There are cases where the PTL is held to ensure no
  26.                  * new threads start up in user mode using an mm, which
  27.                  * allows optimizing out ipis; the tlb_gather_mmu code
  28.                  * is an example.
  29.                  */
  30.                 spin_unlock_wait(&oldmm->page_table_lock);
  31.                 goto good_mm;
  32.         }

  33.         retval = -ENOMEM;
  34.         mm = allocate_mm();/*[color=blue] 由fork()调用,
  35. 为子进程创建新的mm_struct结构。*/[/color]
  36.         if (!mm)
  37.                 goto fail_nomem;

  38.         /* Copy the current MM stuff.. */
  39.         memcpy(mm, oldmm, sizeof(*mm));/*[color=blue] 复制oldmm信息到mm.
  40. 子进程继承父进程全部属性。*/[/color]
  41.         if (!mm_init(mm))/*[color=blue] 初始化mm_struct结构,
  42. 包括处理页面的统计信息,信号量,分配页目录表空间。*/[/color]
  43.                 goto fail_nomem;

  44.         if (init_new_context(tsk,mm))/*[color=blue] 初始化新的上下文,
  45. i386平台为空。*/[/color]
  46.                 goto fail_nocontext;/*[color=blue] 2.6内核的东东??,没看懂...*/[/color]

  47.         retval = dup_mmap(mm, oldmm);/*[color=blue] 将父进程中所有虚拟内存区域
  48. 都复制到子进程中来。*/[/color]
  49.         if (retval)
  50.                 goto free_pt;

  51. good_mm:
  52.         tsk->mm = mm;
  53.         tsk->active_mm = mm;
  54.         return 0;

  55. free_pt:
  56.         mmput(mm);
  57. fail_nomem:
  58.         return retval;

  59. fail_nocontext:
  60.         /*
  61.          * If init_new_context() failed, we cannot use mmput() to free the mm
  62.          * because it calls destroy_context()
  63.          */
  64.         mm_free_pgd(mm);
  65.         free_mm(mm);
  66.         return retval;
  67. }

复制代码
发表于 2004-10-8 01:09:18 | 显示全部楼层

dup_mmap(mm, oldmm);/* 将父进程中所有虚拟内存区域都复制到子进程中来。

\linux-2.6.8\kernel\fork.c

  1.   
  2. static inline int dup_mmap(struct mm_struct * mm, struct mm_struct * oldmm)
  3. {
  4.         struct vm_area_struct * mpnt, *tmp, **pprev;
  5.         struct rb_node **rb_link, *rb_parent;
  6.         int retval;
  7.         unsigned long charge;
  8.         struct mempolicy *pol;

  9.         down_write(&oldmm->mmap_sem);
  10.         flush_cache_mm(current->mm);
  11.         mm->locked_vm = 0;
  12.         mm->mmap = NULL;
  13.         mm->mmap_cache = NULL;
  14.         mm->free_area_cache = TASK_UNMAPPED_BASE;
  15.         mm->map_count = 0;
  16.         mm->rss = 0;
  17.         cpus_clear(mm->cpu_vm_mask);
  18.         mm->mm_rb = RB_ROOT;
  19.         rb_link = &mm->mm_rb.rb_node;
  20.         rb_parent = NULL;
  21.         pprev = &mm->mmap;

  22.         /*
  23.          * Add it to the mmlist after the parent.
  24.          * Doing it this way means that we can order the list,
  25.          * and fork() won't mess up the ordering significantly.
  26.          * Add it first so that swapoff can see any swap entries.
  27.          */
  28.         spin_lock(&mmlist_lock);
  29.         list_add(&mm->mmlist, &current->mm->mmlist);
  30.         mmlist_nr++;
  31.         spin_unlock(&mmlist_lock);

  32.         for (mpnt = current->mm->mmap ; mpnt ; mpnt = mpnt->vm_next) {[color=blue]
  33. /*用for循环全部复制父进程的虚拟内存区域!*/[/color]
  34.                 struct file *file;

  35.                 if(mpnt->vm_flags & VM_DONTCOPY)
  36.                         continue;
  37.                 charge = 0;
  38.                 if (mpnt->vm_flags & VM_ACCOUNT) {
  39.                         unsigned int len = (mpnt->vm_end - mpnt->vm_start) >> PAGE_SHIFT;
  40.                         if (security_vm_enough_memory(len))
  41.                                 goto fail_nomem;
  42.                         charge = len;
  43.                 }
  44.                 tmp = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);[color=blue]
  45. /*为子进程分配vm_area_struct结构*/[/color]
  46.                 if (!tmp)
  47.                         goto fail_nomem;
  48.                 *tmp = *mpnt;[color=blue]
  49. /*直接复制父进程vm_area_struct结构。*/[/color]
  50.                 pol = mpol_copy(vma_policy(mpnt));
  51.                 retval = PTR_ERR(pol);
  52.                 if (IS_ERR(pol))
  53.                         goto fail_nomem_policy;
  54.                 vma_set_policy(tmp, pol);
  55.                 tmp->vm_flags &= ~VM_LOCKED;
  56.                 tmp->vm_mm = mm;
  57.                 tmp->vm_next = NULL;
  58.                 anon_vma_link(tmp);
  59.                 vma_prio_tree_init(tmp);
  60.                 file = tmp->vm_file;
  61.                 if (file) {
  62.                         struct inode *inode = file->f_dentry->d_inode;
  63.                         get_file(file);
  64.                         if (tmp->vm_flags & VM_DENYWRITE)
  65.                                 atomic_dec(&inode->i_writecount);
  66.       
  67.                         /* insert tmp into the share list, just after mpnt */
  68.                         spin_lock(&file->f_mapping->i_mmap_lock);
  69.                         flush_dcache_mmap_lock(file->f_mapping);
  70.                         vma_prio_tree_add(tmp, mpnt);
  71.                         flush_dcache_mmap_unlock(file->f_mapping);
  72.                         spin_unlock(&file->f_mapping->i_mmap_lock);
  73.                 }

  74.                 /*
  75.                  * Link in the new vma and copy the page table entries:
  76.                  * link in first so that swapoff can see swap entries,
  77.                  * and try_to_unmap_one's find_vma find the new vma.
  78.                  */
  79.                 spin_lock(&mm->page_table_lock);
  80.                 *pprev = tmp;
  81.                 pprev = &tmp->vm_next;

  82.                 __vma_link_rb(mm, tmp, rb_link, rb_parent);
  83.                 rb_link = &tmp->vm_rb.rb_right;
  84.                 rb_parent = &tmp->vm_rb;

  85.                 mm->map_count++;
  86.                 retval = copy_page_range(mm, current->mm, tmp);[color=blue]/*将父进程中位于
  87. 内存区域tmp中的页目录,页表复制到新进程中!*/[/color]
  88.                 spin_unlock(&mm->page_table_lock);

  89.                 if (tmp->vm_ops && tmp->vm_ops->open)
  90.                         tmp->vm_ops->open(tmp);

  91.                 if (retval)
  92.                         goto out;
  93.         }
  94.         retval = 0;

  95. out:
  96.         flush_tlb_mm(current->mm);
  97.         up_write(&oldmm->mmap_sem);
  98.         return retval;
  99. fail_nomem_policy:
  100.         kmem_cache_free(vm_area_cachep, tmp);
  101. fail_nomem:
  102.         retval = -ENOMEM;
  103.         vm_unacct_memory(charge);
  104.         goto out;
  105. }




复制代码
发表于 2004-10-8 01:22:31 | 显示全部楼层

copy_page_range()将父进程中位于内存区域tmp中的页目录,页表复制到新进程中!

\linux-2.6.8\mm\memory.c

copy_page_range()这函数太长,以后再看。。头痛...



[PHP]int copy_page_range(struct mm_struct *dst, struct mm_struct *src,
                        struct vm_area_struct *vma)
{
        pgd_t * src_pgd, * dst_pgd;
        unsigned long address = vma->vm_start;
        unsigned long end = vma->vm_end;
        unsigned long cow;

        if (is_vm_hugetlb_page(vma))
                return copy_hugetlb_page_range(dst, src, vma);

        cow = (vma->vm_flags & (VM_SHARED | VM_MAYWRITE)) == VM_MAYWRITE;
        src_pgd = pgd_offset(src, address)-1;
        dst_pgd = pgd_offset(dst, address)-1;

        for (;;) {
                pmd_t * src_pmd, * dst_pmd;

                src_pgd++; dst_pgd++;
               
                /* copy_pmd_range */
               
                if (pgd_none(*src_pgd))
                        goto skip_copy_pmd_range;
                if (unlikely(pgd_bad(*src_pgd))) {
                        pgd_ERROR(*src_pgd);
                        pgd_clear(src_pgd);
skip_copy_pmd_range:        address = (address + PGDIR_SIZE) & PGDIR_MASK;
                        if (!address || (address >= end))
                                goto out;
                        continue;
                }

                src_pmd = pmd_offset(src_pgd, address);
                dst_pmd = pmd_alloc(dst, dst_pgd, address);
                if (!dst_pmd)
                        goto nomem;

                do {
                        pte_t * src_pte, * dst_pte;
               
                        /* copy_pte_range */
               
                        if (pmd_none(*src_pmd))
                                goto skip_copy_pte_range;
                        if (unlikely(pmd_bad(*src_pmd))) {
                                pmd_ERROR(*src_pmd);
                                pmd_clear(src_pmd);
skip_copy_pte_range:
                                address = (address + PMD_SIZE) & PMD_MASK;
                                if (address >= end)
                                        goto out;
                                goto cont_copy_pmd_range;
                        }

                        dst_pte = pte_alloc_map(dst, dst_pmd, address);
                        if (!dst_pte)
                                goto nomem;
                        spin_lock(&src->page_table_lock);       
                        src_pte = pte_offset_map_nested(src_pmd, address);
                        do {
                                pte_t pte = *src_pte;
                                struct page *page;
                                unsigned long pfn;

                                /* copy_one_pte */

                                if (pte_none(pte))
                                        goto cont_copy_pte_range_noset;
                                /* pte contains position in swap, so copy. */
                                if (!pte_present(pte)) {
                                        if (!pte_file(pte))
                                                swap_duplicate(pte_to_swp_entry(pte));
                                        set_pte(dst_pte, pte);
                                        goto cont_copy_pte_range_noset;
                                }
                                pfn = pte_pfn(pte);
                                /* the pte points outside of valid memory, the
                                 * mapping is assumed to be good, meaningful
                                 * and not mapped via rmap - duplicate the
                                 * mapping as is.
                                 */
                                page = NULL;
                                if (pfn_valid(pfn))
                                        page = pfn_to_page(pfn);

                                if (!page || PageReserved(page)) {
                                        set_pte(dst_pte, pte);
                                        goto cont_copy_pte_range_noset;
                                }

                                /*
                                 * If it's a COW mapping, write protect it both
                                 * in the parent and the child
                                 */
                                if (cow) {
                                        ptep_set_wrprotect(src_pte);
                                        pte = *src_pte;
                                }

                                /*
                                 * If it's a shared mapping, mark it clean in
                                 * the child
                                 */
                                if (vma->vm_flags & VM_SHARED)
                                        pte = pte_mkclean(pte);
                                pte = pte_mkold(pte);
                                get_page(page);
                                dst->rss++;
                                set_pte(dst_pte, pte);
                                page_dup_rmap(page);
cont_copy_pte_range_noset:
                                address += PAGE_SIZE;
                                if (address >= end) {
                                        pte_unmap_nested(src_pte);
                                        pte_unmap(dst_pte);
                                        goto out_unlock;
                                }
                                src_pte++;
                                dst_pte++;
                        } while ((unsigned long)src_pte & PTE_TABLE_MASK);
                        pte_unmap_nested(src_pte-1);
                        pte_unmap(dst_pte-1);
                        spin_unlock(&src->page_table_lock);
                        cond_resched_lock(&dst->page_table_lock);
cont_copy_pmd_range:
                        src_pmd++;
                        dst_pmd++;
                } while ((unsigned long)src_pmd & PMD_TABLE_MASK);
        }
out_unlock:
        spin_unlock(&src->page_table_lock);
out:
        return 0;
nomem:
        return -ENOMEM;
}

[/PHP]
您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表