Linux 2.6.12 内核 进程 1 的创建(二)
下面分析一下do_fork() 函数:
`</p>
/* * Ok, this is the main fork-routine. * * It copies the process, and if successful kick-starts * it and waits for it to finish using the VM if required. */ 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 pid = alloc_pidmap(); 为进程申请进程号pid if (pid < 0) return -EAGAIN; 若申请失败返回一个值为(-EAGAIN)的错误号,表示系统资源不足,请稍后重试 if (unlikely(current->ptrace)) { trace = fork_traceflag (clone_flags); if (trace) clone_flags |= CLONE_PTRACE; } 判断当前进程是否处于被跟踪状态。如果当前进程处于被跟踪调试状态且传进来的实际参数 clone_flags 没有使用 CLONE_UNPTRACED 强调子进程不可被跟踪,那么在标记 clone_flags 中添加标记 CLONE_PTRACE ,使创建的进程也处于被跟踪调试状态! 此时current 为进程0 ,current->ptrace = 0 没有处于被跟踪调试状态! p = copy_process(clone_flags, stack_start, regs, stack_size, parent_tidptr, child_tidptr, pid); 完成具体进程创建工作。该函数创建一个但前进程的进程描述符的拷贝(内容不完全相同)作为新创建进程描述符,并将描述符地址返回! /* * Do this prior waking up the new thread - the thread pointer * might get invalid after that point, if the thread exits quickly. */ if (!IS_ERR(p)) { 判断返回的描述符是否正确! struct completion vfork; if (clone_flags & CLONE_VFORK) { p->vfork_done = &vfork; init_completion(&vfork); } if ((p->ptrace & PT_PTRACED) || (clone_flags & CLONE_STOPPED)) { /* * We'll start up with an immediate SIGSTOP. */ sigaddset(&p->pending.signal, SIGSTOP); set_tsk_thread_flag(p, TIF_SIGPENDING); } 若存在以上标志,在新建进程处理链表中添加信号SIGSTOP,并设置进程的有信号处理标记 TIF_SIGPENDING,这样在进程 获得处理器时会立即处理信号 SIGSTOP,此时进程会立即放弃处理器进入睡眠状态! if (!(clone_flags & CLONE_STOPPED)) wake_up_new_task(p, clone_flags); else p->state = TASK_STOPPED; 若含有CLONE_STOPPED 标记则将新创建进程状态设置为 TASK_STOPPED ,说明新创建的进程不会运行,除非有进程通过信号唤醒它! 若没有 把当前进程添加到当前处理器队列中! wake_up_new_task() 函数最后分析! if (unlikely (trace)) { current->ptrace_message = pid; ptrace_notify ((trace << 8) | SIGTRAP); } if (clone_flags & CLONE_VFORK) { wait_for_completion(&vfork); if (unlikely (current->ptrace & PT_TRACE_VFORK_DONE)) ptrace_notify ((PTRACE_EVENT_VFORK_DONE << 8) | SIGTRAP); } 若包含 CLONE_VFORK ,则将当前进程添加到新创建子进程的进程描述符中成员变量 vfork_done 包含的等待队列中,在子进程退出或通过系统调用 exec() 执行其他程序时,激活该等待队列中的进程! } else { 若失败释放到申请的进程描述符 free_pidmap(pid); pid = PTR_ERR(p); } return pid; 返回 进程号 }`
static inline long IS_ERR(const void *ptr) { return IS_ERR_VALUE((unsigned long)ptr); } #define IS_ERR_VALUE(x) unlikely((x) > (unsigned long)-1000L)
若描述符地址(线性地址)大于FFFFFC18 则说明描述符创建错误!
书上说此处有一个书写bug !在内核初始化内存时,在0xFFFFF000 ~ 0xFFFFFFFF之间保留了一个物理页面大小的物理空间用于捕捉内核错误 (unsigned long)-1000L 应该为 (unsigned long)-0x1000L!(因为 -0x1000 才是 0xFFFFF000)