日期:2014-05-16 浏览次数:20883 次
通常我们在代码中调用fork()来创建一个进程或者调用pthread_create()来创建一个线程,创建一个进程需要为其分配内存资源,文件资源,时间片资源等,在这里来描述一下linux进程的创建过程及写时复制技术。
一写时复制
子进程和父进程通常拥有着不同的进程内存空间(线程除外),传统的unix在创建子进程后,会复制父进程的地址空间的所有内容,这就十分的低效,因为经常子进程会立即执行exec操作,创建一个崭新的内存空间,另外像进程代码段这样的内存,父子进程只是读,而没有写操作,完全可以共享,而不用去复制,这样会节省大量的时间。fork(),vfork(),clone()三个系统调用最后都是使用sys_clone()服务例程来完成了系统调用,sys_clone()服务例程会去调用do_fork()函数,主要的处理流程就在do_fork()中。
3.1do_fork()
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;
//查看pidmap_array查找新进程的pid
long pid = alloc_pidmap();
//查看当前进程是否被trace,设置新进程的trace状态,只要进程不是内核线程就应该被trace
if (unlikely(current->ptrace)) {
trace = fork_traceflag (clone_flags);
if (trace)
clone_flags |= CLONE_PTRACE;
}
//将父进程的相关信息,如地址空间,信号等信息复制到新建进程描述符上面
p = copy_process(clone_flags, stack_start, regs, stack_size, parent_tidptr, child_tidptr, pid);
if (!IS_ERR(p)) {
struct completion vfork;
//如果该进程是vfork出来的,需要等待子进程结束或退出后再执行父进程
if (clone_flags & CLONE_VFORK) {
p->vfork_done = &vfork;
init_completion(&vfork);
}
//如果该进程被追踪,或者设置了clone_stopped标记,给该进程发送STOP信号, 设置了CLONE_STOPPED标记的话,进程不能够立即执行,需要先stop下来,后面通过
向该进程发送SIG_CONT信号使其恢复执行
if ((p->ptrace & PT_PTRACED) || (clone_flags & CLONE_STOPPED)) {
sigaddset(&p->pending.signal, SIGSTOP);
set_tsk_thread_flag(p, TIF_SIGPENDING);
}
//未设置clone_stopped标记,唤醒新进程,此后新进程可以参与调度
if (!(clone_flags & CLONE_STOPPED))
wake_up_new_task(p, clone_flags);
else
p->state = TASK_STOPPED;
++total_f