Chinaunix首页 | 论坛 | 博客
  • 博客访问: 90576
  • 博文数量: 16
  • 博客积分: 1113
  • 博客等级: 少尉
  • 技术积分: 170
  • 用 户 组: 普通用户
  • 注册时间: 2010-03-31 13:36
文章分类

全部博文(16)

文章存档

2011年(4)

2010年(12)

分类: LINUX

2010-08-10 18:01:11

下面分析一下do_fork() 函数:

 

/*
 * 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)) {

    判断返回的描述符是否正确!

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)


        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;

    返回 进程号
}

 

                                                                                                                     参考:深入理解Linux内核

                                                                                                                                                                                                                                                          Linux 内核标准教程


阅读(1443) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~