1、linux创建进程的方式是先通过调用fork创建一个和调用进程基本一样的子进程,二者之间的区别在于PID和PPID不同。然后子进程调用exec函数装载一个新的进程到地址空间执行。
其他的操作系统产生子进程的方式是spawn:在新的地址空间中创建进程,然后载入可执行文件执行。
2、传统的fork是将所有的资源都复制给新的进程,但linux使用了COW(copy-on-write)技术,只有在需要写入的时候才复制整个地址空间。这样做的好处就是当子进程产生之后如果立刻调用exec就不必复制了。
3、fork,vfork,__clone都是通过传给clone不同的参数来实现的。clone调用do_fork来实现进程的创建。
do_fork函数
①调用copy_process来完成进程的复制
p = copy_process(clone_flags, stack_start, regs, stack_size,child_tidptr, NULL, trace);
②调用wake_up_new_task让子进程先执行
wake_up_new_task(p, clone_flags);
③返回新创建进程的pid
copy_process函数
①根据clone_flags执行一些检查
if ((clone_flags & (CLONE_NEWNS|CLONE_FS)) == (CLONE_NEWNS|CLONE_FS))
return ERR_PTR(-EINVAL);
if ((clone_flags & CLONE_THREAD) && !(clone_flags & CLONE_SIGHAND))
return ERR_PTR(-EINVAL);
if ((clone_flags & CLONE_SIGHAND) && !(clone_flags & CLONE_VM))
return ERR_PTR(-EINVAL);
if ((clone_flags & CLONE_PARENT) &&
current->signal->flags & SIGNAL_UNKILLABLE)
return ERR_PTR(-EINVAL);
retval = security_task_create(clone_flags);
if (retval)
goto fork_out;
retval = -ENOMEM;
②调用dup_task_struck函数复制进程描述符和thread_info,此时父子进程完全一样
p = dup_task_struct(current);
if (!p)
goto fork_out;
③执行检查判断是否超出给他分配的资源的限制
if (atomic_read(&p->real_cred->user->processes) >=
p->signal->rlim[RLIMIT_NPROC].rlim_cur) {
if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RESOURCE) &&
p->real_cred->user != INIT_USER)
goto bad_fork_free;
}
④初始化子进程描述符中的各个值,包括:
⑤返回进程描述符
dup_task_struck函数
①调用alloc_task_struct分配一块进程描述符的空间
tsk = alloc_task_struct();
if (!tsk)
return NULL;
②调用alloc_thread_info分配一个thread_info
ti = alloc_thread_info(tsk);
if (!ti) {
free_task_struct(tsk);
return NULL;
}
③将父进程的进程描述符和thread_info赋值给新创建的进程描述符与thread_info
*ti = *orig->thread_info;
*tsk = *orig;
tsk->thread_info = ti;
ti->task = tsk;