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)