请问初始化新进程的函数在哪里?
时间:2004-09-27
来源:互联网
大家好,请问初始化新进程的函数在哪里?Linux 中的 do_fork() 似乎和 UNIX 中的 fork() 不太一样。
作者: herberteuler 发布时间: 2004-09-27
Linux 综合使用了BSD的vfork()和SystemV的fork()COW技术,
fork和vfork都是调用同一个核心函数do_fork。
fork和vfork都是调用同一个核心函数do_fork。
作者: 锋锋 发布时间: 2004-09-30
UNIX V6 的 fork() 函数调用了 newproc() 来初始化页表,我想读读 Linux 中的对应代码,可是没有找到。虽然 Linux 中使用了动态的结构,但原理应该一样吧,它在哪里呢?谢谢。
作者: herberteuler 发布时间: 2004-09-30
很久没讨论了。。
这段时间看了一下linux内核方面内容,写一点,不对的地方请各位指正。
重点讨论linux fork()系统调用虚拟地址空间的重建过程:
上面这些摘自《Linux编程白皮书》中描述的。
原理也就这样。
这段时间看了一下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将产生该内存的一个副本,并修改两个进程的页表
和虚拟内存数据结构。
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将产生该内存的一个副本,并修改两个进程的页表
和虚拟内存数据结构。
原理也就这样。
作者: 锋锋 发布时间: 2004-10-08
fork系统调用内部调用copy_mm()实现虚拟地址空间的复制。
\linux-2.6.8\kernel\fork.c
\linux-2.6.8\kernel\fork.c
代码:
static int copy_mm(unsigned long clone_flags, struct task_struct * tsk) { struct mm_struct * mm, *oldmm; int retval; tsk->min_flt = tsk->maj_flt = 0; tsk->cmin_flt = tsk->cmaj_flt = 0; tsk->nvcsw = tsk->nivcsw = tsk->cnvcsw = tsk->cnivcsw = 0; tsk->mm = NULL; tsk->active_mm = NULL; /* 初始化tsk中(子进程) 一些mm相关域*/ /* * Are we cloning a kernel thread? * * We need to steal a active VM for that.. */ oldmm = current->mm; if (!oldmm) return 0; if (clone_flags & CLONE_VM) { atomic_inc(&oldmm->mm_users); mm = oldmm;/* 如由vfork()调用, 直接将父进程mm_struct结构(oldmm = current->mm)返回给子进程,父子共享!*/ /* * There are cases where the PTL is held to ensure no * new threads start up in user mode using an mm, which * allows optimizing out ipis; the tlb_gather_mmu code * is an example. */ spin_unlock_wait(&oldmm->page_table_lock); goto good_mm; } retval = -ENOMEM; mm = allocate_mm();/* 由fork()调用, 为子进程创建新的mm_struct结构。*/ if (!mm) goto fail_nomem; /* Copy the current MM stuff.. */ memcpy(mm, oldmm, sizeof(*mm));/* 复制oldmm信息到mm. 子进程继承父进程全部属性。*/ if (!mm_init(mm))/* 初始化mm_struct结构, 包括处理页面的统计信息,信号量,分配页目录表空间。*/ goto fail_nomem; if (init_new_context(tsk,mm))/* 初始化新的上下文, i386平台为空。*/ goto fail_nocontext;/* 2.6内核的东东??,没看懂...*/ retval = dup_mmap(mm, oldmm);/* 将父进程中所有虚拟内存区域 都复制到子进程中来。*/ if (retval) goto free_pt; good_mm: tsk->mm = mm; tsk->active_mm = mm; return 0; free_pt: mmput(mm); fail_nomem: return retval; fail_nocontext: /* * If init_new_context() failed, we cannot use mmput() to free the mm * because it calls destroy_context() */ mm_free_pgd(mm); free_mm(mm); return retval; }
作者: 锋锋 发布时间: 2004-10-08
\linux-2.6.8\kernel\fork.c
代码:
static inline int dup_mmap(struct mm_struct * mm, struct mm_struct * oldmm) { struct vm_area_struct * mpnt, *tmp, **pprev; struct rb_node **rb_link, *rb_parent; int retval; unsigned long charge; struct mempolicy *pol; down_write(&oldmm->mmap_sem); flush_cache_mm(current->mm); mm->locked_vm = 0; mm->mmap = NULL; mm->mmap_cache = NULL; mm->free_area_cache = TASK_UNMAPPED_BASE; mm->map_count = 0; mm->rss = 0; cpus_clear(mm->cpu_vm_mask); mm->mm_rb = RB_ROOT; rb_link = &mm->mm_rb.rb_node; rb_parent = NULL; pprev = &mm->mmap; /* * Add it to the mmlist after the parent. * Doing it this way means that we can order the list, * and fork() won't mess up the ordering significantly. * Add it first so that swapoff can see any swap entries. */ spin_lock(&mmlist_lock); list_add(&mm->mmlist, ¤t->mm->mmlist); mmlist_nr++; spin_unlock(&mmlist_lock); for (mpnt = current->mm->mmap ; mpnt ; mpnt = mpnt->vm_next) { /*用for循环全部复制父进程的虚拟内存区域!*/ struct file *file; if(mpnt->vm_flags & VM_DONTCOPY) continue; charge = 0; 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; } tmp = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL); /*为子进程分配vm_area_struct结构*/ if (!tmp) goto fail_nomem; *tmp = *mpnt; /*直接复制父进程vm_area_struct结构。*/ pol = mpol_copy(vma_policy(mpnt)); retval = PTR_ERR(pol); if (IS_ERR(pol)) goto fail_nomem_policy; vma_set_policy(tmp, pol); tmp->vm_flags &= ~VM_LOCKED; tmp->vm_mm = mm; tmp->vm_next = NULL; anon_vma_link(tmp); vma_prio_tree_init(tmp); file = tmp->vm_file; if (file) { struct inode *inode = file->f_dentry->d_inode; get_file(file); if (tmp->vm_flags & VM_DENYWRITE) atomic_dec(&inode->i_writecount); /* insert tmp into the share list, just after mpnt */ spin_lock(&file->f_mapping->i_mmap_lock); flush_dcache_mmap_lock(file->f_mapping); vma_prio_tree_add(tmp, mpnt); flush_dcache_mmap_unlock(file->f_mapping); spin_unlock(&file->f_mapping->i_mmap_lock); } /* * Link in the new vma and copy the page table entries: * link in first so that swapoff can see swap entries, * and try_to_unmap_one's find_vma find the new vma. */ spin_lock(&mm->page_table_lock); *pprev = tmp; pprev = &tmp->vm_next; __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, current->mm, tmp);/*将父进程中位于 内存区域tmp中的页目录,页表复制到新进程中!*/ spin_unlock(&mm->page_table_lock); if (tmp->vm_ops && tmp->vm_ops->open) tmp->vm_ops->open(tmp); if (retval) goto out; } retval = 0; out: flush_tlb_mm(current->mm); up_write(&oldmm->mmap_sem); return retval; fail_nomem_policy: kmem_cache_free(vm_area_cachep, tmp); fail_nomem: retval = -ENOMEM; vm_unacct_memory(charge); goto out; }
作者: 锋锋 发布时间: 2004-10-08
\linux-2.6.8\mm\memory.c
copy_page_range()这函数太长,以后再看。。头痛...:(
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;
}
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;
}
作者: 锋锋 发布时间: 2004-10-08
相关阅读 更多
热门阅读
-
office 2019专业增强版最新2021版激活秘钥/序列号/激活码推荐 附激活工具
阅读:74
-
如何安装mysql8.0
阅读:31
-
Word快速设置标题样式步骤详解
阅读:28
-
20+道必知必会的Vue面试题(附答案解析)
阅读:37
-
HTML如何制作表单
阅读:22
-
百词斩可以改天数吗?当然可以,4个步骤轻松修改天数!
阅读:31
-
ET文件格式和XLS格式文件之间如何转化?
阅读:24
-
react和vue的区别及优缺点是什么
阅读:121
-
支付宝人脸识别如何关闭?
阅读:21
-
腾讯微云怎么修改照片或视频备份路径?
阅读:28