进程的创建
-
进程创建流程
a. do_fork主要处理clone、fork、vfork系统调用 1. 先检查父进程的ptrace字段,如果父进程被跟踪了,则根据clone_flag的信息,对子进程进行相关操作 2. 调用copy_process()函数将fork()之前的信息复制一份给 子进程。这里包含了出现异常nr=0的情况 3. 如果是vfork的话,直接初始化完成处理信息。 4. 用wake_up_new_task()函数将新创建的进程加入到调度器中,为其分配CPU。 5. 如果是vfork(),父进程会等待子进程结束或者子进程调用exec函数族。 6. 最后返回子进程的pid b. copy_process 创建进程描述符以及子进程执行需要的其他的数据结构 1. 进行一些检查 2. 创建一些进程需要的结构体 3. 复制父进程的task_struct 4. 初始化进程状态,为进程分配cpu 5. 初始化进程内核栈 3. 返回创建进程的进程描述符的地址 c. dup_task_struct 1. 用alloc_stack_node分配一个task_struct节点 2. 用alloc_thread_info_node分配一个thread_info节点,其实是分配了一个thread_union联合体,将栈底返回给ti d. copy_thread(解释如下的问题) 1. 为什么 fork 在子进程中返回0,原因是childregs->ax = 0;这段代码将子进程的 eax 赋值为0 2. p->thread.ip = (unsigned long) ret_from_fork;将子进程的 ip 设置为 ret_form_fork 的首地址,因此子进程是从 ret_from_fork 开始执行的 e. sched_fork 1. 设置子进程的状态为TASK_RUNNING 2. 为子进程分配CPU
-
sys_clone
asmlinkage int sys_clone(unsigned long __user *args) { unsigned long clone_flags; //各种各样的信息,低字节指定进程结束时发送到父进程的信号代码,通常选择SIGCHLD信号,剩余的3字节给一clone标志组,用于编码 unsigned long newsp; //根据do_fork的参数,这个是新的进程的栈地址 uintptr_t parent_tidptr; //父进程用户态变量的地址 uintptr_t child_tidptr; //表示新的轻量级进程的用户态变量地址 get_user(clone_flags, &args[0]); get_user(newsp, &args[1]); get_user(parent_tidptr, &args[2]); get_user(child_tidptr, &args[3]); return do_fork(clone_flags, newsp, 0, (int __user *)parent_tidptr, (int __user *)child_tidptr); }
-
do_fork
long do_fork(unsigned long clone_flags, unsigned long stack_start, unsigned long stack_size, //默认为0,未使用 其余参数都是clone传进来的 int __user *parent_tidptr, int __user *child_tidptr) { return _do_fork(clone_flags, stack_start, stack_size, parent_tidptr, child_tidptr, 0); }
-
_do_fork
/* 执行流程 1. 先检查父进程的ptrace字段,如果父进程被跟踪了,则根据clone_flag的信息,对子进程进行相关操作 2. 调用copy_process()函数将fork()之前的信息复制一份给子进程。这里包含了出现异常nr=0的情况 3. 如果是vfork的话,直接初始化完成处理信息。 4. 用wake_up_new_task()函数将新创建的进程加入到调度器中,为其分配CPU。 5. 如果是vfork(),父进程会等待子进程结束或者子进程调用exec函数族。 6. 最后返回子进程的pid */ long _do_fork(unsigned long clone_flags, unsigned long stack_start, unsigned long stack_size, int __user *parent_tidptr, int __user *child_tidptr, unsigned long tls) { struct task_struct *p; int trace = 0; long nr; /* * Determine whether and which event to report to ptracer. When * called from kernel_thread or CLONE_UNTRACED is explicitly * requested, no event is reported; otherwise, report if the event * for the type of forking is enabled. */ //检查父进程的ptrace字段,如果父进程的ptrace != 0,则根据情况设置新的进程的trace值 if (!(clone_flags & CLONE_UNTRACED)) { //父进程被跟踪的情况 if (clone_flags & CLONE_VFORK) // if the parent wants the child to wake it up on mm_release trace = PTRACE_EVENT_VFORK; else if ((clone_flags & CSIGNAL) != SIGCHLD) trace = PTRACE_EVENT_CLONE; else trace = PTRACE_EVENT_FORK; if (likely(!ptrace_event_enabled(current, trace))) trace = 0; } //复制进程描述符,如果所有资源可用, 返回刚创建的task_struct描述符的地址 p = copy_process(clone_flags, stack_start, stack_size, child_tidptr, NULL, trace, tls, NUMA_NO_NODE); add_latent_entropy(); /* * Do this prior waking up the new thread - the thread pointer * might get invalid after that point, if the thread exits quickly. */ //copy_process的时候没有出错的话 if (!IS_ERR(p)) { struct completion vfork; struct pid *pid; trace_sched_process_fork(current, p); //子进程的pid pid = get_task_pid(p, PIDTYPE_PID); //子进程的全局进程号 nr = pid_vnr(pid); //设置父进程的TID if (clone_flags & CLONE_PARENT_SETTID) put_user(nr, parent_tidptr); //首先定义了一个完成量vfork,如果clone_flags包含CLONE_VFORK标志,那么将进程描述符中的vfork_done字段指向这个完成量,之后再对vfork完成量进行初始化。vfork完成量所起到的作用:当子进程调用exec函数或退出时就向父进程发出信号。此时,父进程才会被唤醒;否则一直等待。 if (clone_flags & CLONE_VFORK) { p->vfork_done = &vfork; init_completion(&vfork); get_task_struct(p); } //将子进程加入到调度器中,为其分配 CPU,准备执行 wake_up_new_task(p); /* forking complete and child started to run, tell ptracer */ if (unlikely(trace)) ptrace_event_pid(trace, pid); //如果CLONE_VFORK标志被设置,则通过wait操作将父进程阻塞,直至子进程调用exec函数或者退出 if (clone_flags & CLONE_VFORK) { if (!wait_for_vfork_done(p, &vfork)) ptrace_event_pid(PTRACE_EVENT_VFORK_DONE, pid); } put_pid(pid); } else { nr = PTR_ERR(p); }