fork()系统调用对应的内核实现为sys_fork(),sys_fork()是对do_fork()的简单封装,sys_fork()的任务是从处理器寄存器中提取由用户空间提供的信息,do_fork()负责进程的复制。fork()和clone()系统调用的入口点sys_vfork()和sys_clone()也是调用的do_fork()。
do_fork()函数中主要的处理流程如下所示(引自《深入Linux内核架构》):
我们关注的是用户进程的创建,所以这里只关注copy_process()和wake_up_new_task()这两个函数。
copy_process()函数有7个参数,其中我们需要关心的有clone_flags、stack_start和regs。cone_flags是一个标志集合,分为两部分:最低的字节指定了在子进程终止时发送给父进程的信号,其余的高位字节保存了各种真正的复制标志,如CLONE_FS、CLONE_THREAD等。在用户层调用fork()时不能指定标志,所以默认的CLONE_FLAGS的值为SIGCHLD。如果你想要修改默认的创建进程的方式,或者修改子进程退出时的信号,可以使用clone()系统调用(和fork不同,具体参见man clone)。stack_start是父进程(也就是current)的用户栈的起始地址。regs是一个指向寄存器集合的指针,该参数使用的数据类型是特定于体系结构的struct pt_regs。
现在我们来看copy_process()是创建子进程的。
1、标志检查
copy_process()首先会检查clone_flags中指定的标志是否冲突以及安全检查,创建用户进程时只有SIGCHLD,所以这个检查是肯定没有问题的。
2、dup_task_struct()
我们知道Linux内核中使用task_struct结构来表示进程,子进程的描述符结构是在du_task_struct()中创建的,其源码实现如下:
static struct task_struct *dup_task_struct(struct task_struct *orig)
{