读书是一个由厚读薄,再由薄读厚的过程。孟宁的《Linux内核分析》在课上把繁杂庞大的Linux内核抽像出主脉络,本文试图探索第六课中创建进程这一主题迷的途幽径。同时本文也是《第四讲 进程管理 》的补充。
一般操作系统都有类似于create_task()之类的API来凭空创造出进程。Linux沿袭自Unix,采用了另一种思路,即fork+exec。所有进程都先由一个现有进程复制而来,再改变一些属性形成新进程。本文主要探讨fork()
。
do_fork
fork()
的功能类似于细胞分裂,从父进程中复制出一个子进程,与父进程有同样的代码、数据和打开文件指针等,但拥有自己的task_struct结构和内核堆栈等。
Linux有fork()
和clone()
两个系统调用,后来又增加了一个vfork()
。三者略有细节上的差异但本质相同。它们最终都是通过do_fork()
函数处理。因此do_fork()
就是这一问题中的关键。它的实现脉络比较清晰,我们来逐行分析:
=============== kernel/fork.c 1384 1489 ==================
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;
}
/*
* We hope to recycle these flags after 2.6.26
*/
if (unlikely(clone_flags & CLONE_STOPPED)) {
static int __read_mostly count = 100;
if (count > 0 && printk_ratelimit()) {
char comm[TASK_COMM_LEN];
count--;
printk(KERN_INFO "fork(): process `%s' used deprecated "
"clone flags 0x%lx\n",
get_task_comm(comm, current),
clone_flags & CLONE_STOPPED);
}
}
/