Linux下用于创建进程的API有三个fork,vfork和clone,这三个函数分别是通过系统调用sys_fork,sys_vfork以及sys_clone实现的
注:这里目前讨论的都是基于x86架构的
fork
- int sys_fork(struct pt_regs *regs)
- {
- return do_fork(SIGCHLD, regs->sp, regs, 0, NULL, NULL);
- }
vfork
- int sys_vfork(struct pt_regs *regs)
- {
- return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->sp, regs, 0,
- NULL, NULL);
- }
clone
- int sys_vfork(struct pt_regs *regs)
- {
- return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->sp, regs, 0,
- NULL, NULL);
- }
由以上代码可知,最终这三个系统调用,都是通过do_fork来实现的,只是传入了不同的参数。
那么接下来先看看do_fork的实现
- long do_fork(unsigned long clone_flags,
-
unsigned long stack_start,
-
struct pt_regs *regs,
-
unsigned long stack_size,
-
int __user *parent_tidptr,
-
int __user *child_tidptr)
-
{
-
struct task_struct *p;
-
int trace = 0;
-
long nr;
-
-
/*
-
* Do some preliminary argument and permissions checking before we
-
* actually start allocating stuff
-
*/
-
if (clone_flags & CLONE_NEWUSER) {
-
if (clone_flags & CLONE_THREAD)
-
return -EINVAL;
-
/* hopefully this check will go away when userns support is
-
* complete
-
*/
-
if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SETUID) ||
-
!capable(CAP_SETGID))
-
return -EPERM;
-
}
-
-
/*
-
* When called from kernel_thread, don't do user tracing stuff.
-
*/
-
if (likely(user_mode(regs)))
-
trace = tracehook_prepare_clone(clone_flags);
- 1)
-
p = copy_process(clone_flags, stack_start, regs, stack_size,
-
child_tidptr, NULL, trace);
-
/*
-
* Do this prior waking up the new thread - the thread pointer
-
* might get invalid after that point, if the thread exits quickly.
-
*/
-
if (!IS_ERR(p)) {
-
struct completion vfork;
-
-
trace_sched_process_fork(current, p);
-
2)
-
nr = task_pid_vnr(p);
-
-
if (clone_flags & CLONE_PARENT_SETTID)
-
put_user(nr, parent_tidptr);
-
-
if (clone_flags & CLONE_VFORK) {
-
p->vfork_done = &vfork;
-
init_completion(&vfork);
-
}
-
-
audit_finish_fork(p);
-
tracehook_report_clone(regs, clone_flags, nr, p);
-
-
/*
-
* We set PF_STARTING at creation in case tracing wants to
-
* use this to distinguish a fully live task from one that
-
* hasn't gotten to tracehook_report_clone() yet. Now we
-
* clear it and set the child going.
-
*/
-
p->flags &= ~PF_STARTING;
-
-
wake_up_new_task(p, clone_flags);
-
-
tracehook_report_clone_complete(trace, regs,
-
clone_flags, nr, p);
-
-
if (clone_flags & CLONE_VFORK) {
-
freezer_do_not_count();
-
wait_for_completion(&vfork);
-
freezer_count();
-
tracehook_report_vfork_done(p, nr);
-
}
-
} else {
-
nr = PTR_ERR(p);
-
}
-
return nr;
-
}
代码不是很长,但是涉及到很多工作,这里简单讲解一下主体流程,细节方面的东西后面再解释:
1、copy_process:这个步骤主要是执行生成新进程的主要工作,并根据指定的标志重用父进程的一些数据;
- p = copy_process(clone_flags, stack_start, regs, stack_size,
-
child_tidptr, NULL, trace);
2、确定PID:注意这里不是生成新的PID,而是确定局部PID;
3、init_completion:初始化vfork的完成处理程序(即是vfork调用时设置的CLONE_VFORK标志)和ptrace标志;
- if (clone_flags & CLONE_VFORK) {
-
p->vfork_done = &vfork;
-
init_completion(&vfork);
-
}
4、wake_up_new_task:这个是保证子进程在父进程之前调用,如果子进程立即调用exec,这样可以极大的减少复制内存页的工作量;
- wake_up_new_task(p, clone_flags);
5、wait_for_completion:跟上面的init_completion对应,让父进程在该task_struct的vfork_done成员上进入睡眠状态,直到子进程退出或者执行exec调用时,内核才会调用complete(vfork_done),这样就会唤醒父进程(也包含其他因该变量睡眠的进程)。
- if (clone_flags & CLONE_VFORK) {
-
freezer_do_not_count();
-
wait_for_completion(&vfork);
-
freezer_count();
-
tracehook_report_vfork_done(p, nr);
-
}
这中间还有很多的细节没有讲解,这里主要梳理了一下大致的流程。
接下来会对上面的流程一一进行讲解。
阅读(3349) | 评论(0) | 转发(2) |