clone(), fork(), vfork() 最终都是调用 do_fork()来实现的。 do_fork()在Kernel\fork.c文件中。代码如下:
/*
* Ok, this is the main fork-routine.
*
* It copies the process, and if successful kick-starts
* it and waits for it to finish using the VM if required.
*/
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 pid = alloc_pidmap(); //通过pidmap_array位图,为子进程分配新的PID
if (pid < 0)
return -EAGAIN;
if (unlikely(current->ptrace)) { //检测父进程是否被跟踪,并设置子进程的跟踪
trace = fork_traceflag (clone_flags);
if (trace)
clone_flags |= CLONE_PTRACE;
}
//最关键的一步,复制进程描述符。如果成功,则返回刚刚创建的task_struct描述符地址。下一篇博客会详细介绍
p = copy_process(clone_flags, stack_start, regs, stack_size, parent_tidptr, child_tidptr, pid);
/*
* 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;
if (clone_flags & CLONE_VFORK) {
p->vfork_done = &vfork;
init_completion(&vfork);
}
if ((p->ptrace & PT_PTRACED) || (clone_flags & CLONE_STOPPED)) { //设置了COLNE_STOPPED或者必须跟踪子进程
/*
* We'll start up with an immediate SIGSTOP.
*/
sigaddset(&p->pending.signal, SIGSTOP); //为子进程设置挂起信号
set_tsk_thread_flag(p, TIF_SIGPENDING);
}
if (!(clone_flags & CLONE_STOPPED)) //没有设置COLNE_STOPPED
wake_up_new_task(p, clone_flags); //把子进程插入父进程的运行队列
else
p->state = TASK_STOPPED; //将子进程的进程描述符中的状态设置为 stopped
++total_forks;
if (unlikely (trace)) { //还是设置子进程是否被跟踪。
current->ptrace_message = pid;
ptrace_notify ((trace << 8) | SIGTRAP);
}
if (clone_flags & CLONE_VFORK) { //如果设置了CLONE_VFORK标志,则把父进程插入到等待队列,并挂起父进程,直到子进程释放自己的内存空间
wait_for_completion(&vfork); //也就是说,直到子进程结束或执行新的程序。
if (unlikely (current->ptrace & PT_TRACE_VFORK_DONE))
ptrace_notify ((PTRACE_EVENT_VFORK_DONE << 8) | SIGTRAP);
}
} else {
free_pidmap(pid);
pid = PTR_ERR(p);
}
return pid;
}
参数:clone 包含了很多的信息,如:是否共享内存描述符和页表,是否共享打开的文件表,是否调用vfork(),是否和父进程一样被跟踪。
参数:stack_start 是
参数:regs 是指向通用寄存器值的指针,通用寄存器的只是从用户态切换到内核态时被保存到内核态堆栈的。 个人感觉,就是TSS。
参数:stack_size 未使用
参数:parent_tidptr 是父进程的用户态变量地址
参数:child_tidptr 是子进程的用户态变量地址
CLONE_PTRACE 是clone_flag中的一个标志位,表示如果父进程被跟踪,那么子进程也被跟踪。