文章目录
进程的创建:系统调用fork(),vfork()和clone()
任何进程都是由其他进程创建的。操作系统通过系统调用fork(),vfork()和clone()函数来完成进程的创建。
- 这三个系统调用最终都调用了内核函数do_fork()
- 这三个函数的唯一区别在于随后调用do_fork(0时设置的标志不同。
fork函数的执行过程
fork函数的执行过程:普通程序调用fork()–>库函数fork()–>系统调用(fork功能号)–>由功能号在 sys_call_table[]中寻到sys_fork()函数地址–>调用sys_fork(),这就完成了从用户态到内核态的变化过程。所以,实际上,fork函数对应的实现就是sys_fork。
下面我们具体来看一下内核中sys_fork的实现
asmlinkage int sys_fork(struct pt_regs regs)
{
return do_fork(SIGCHLD, regs.esp, ®s, 0, NULL, NULL);
}
可以看到sys_fork()实际是调用了do_fork()这个函数。真正的fork实现就在do_fork()这个函数中。
do_fork()实现过程
- 首先为子进程申请一个pid
通过查找pidmap_array位图来获取pid
long pid = alloc_pidmap();
- 检查父进程是否允许被跟踪
if (unlikely(current->ptrace)) {
trace = fork_traceflag (clone_flags);
if (trace)
clone_flags |= CLONE_PTRACE;
}
追踪功能在处理进程的函数中普遍使用。在这里我们介绍ptrace功能。
为了确定子进程是否被跟踪,fork_traceflag()必须检查clone_flags的值。
子进程会被跟踪的条件:
- 如果在clone_flags中设置了CLONE_VFORK标志,并且SIGCHLD信号没有被父进程捕获
- 如果当前进程也设置了PT_TRACE_FORK标志,那么子进程就会被跟踪。
- 创建新进程并复制寄存器的值
p = copy_process(clone_flags, stack_start, regs, stack_size, parent_tidptr, child_tidptr, pid);
这是创建进程的关键步骤,稍后会详细讨论相关细节
- 检查do_fork()是否被vfork()所调用
if (!IS_ERR(p)) {
struct completion vfork;
if (clone_flags & CLONE_VFORK) {
p->vfork_done = &vfork;
init_completion(&vfork);
}
/*
* 如果父进程被跟踪或克隆操作被设置成CLONE_STOPPED标志
* 那么子进程在启动时将会收到一个SIGSTOP信号
* 这样子子进程就是以暂停状态启动的
*/
if ((p->ptrace & PT_PTRACED) || (clone_flags & CLONE_STOPPED)) {
/*
* We'll start up with an immediate SIGSTOP.
*/
sigaddset(&p->pending.signal, SIGSTOP);
set_tsk_thread_flag<