C- _do_fork()如何返回两个不同的PID(一个用于父进程,一个用于子过程)



我正在查看 _do_fork()函数((试图了解 fork()如何返回父程进程的孩子pid,而在子过程中为0。

我认为nr包含子过程的PID(将返回到呼叫者过程(,但是我看不到它如何返回0.

答案叉((如何知道何时返回0?说返回值是在为新过程创建的堆栈上传递的,但是(此外,(除了真正理解它之外(,我在代码中找不到。。

so,何处为子过程设置0的返回值吗?

_do_fork()函数的代码在下面复制:

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.
     */
    if (!(clone_flags & CLONE_UNTRACED)) {
        if (clone_flags & CLONE_VFORK)
            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;
    }
    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.
     */
    if (!IS_ERR(p)) {
        struct completion vfork;
        struct pid *pid;
        trace_sched_process_fork(current, p);
        pid = get_task_pid(p, PIDTYPE_PID);
        nr = pid_vnr(pid);
        if (clone_flags & CLONE_PARENT_SETTID)
            put_user(nr, parent_tidptr);
        if (clone_flags & CLONE_VFORK) {
            p->vfork_done = &vfork;
            init_completion(&vfork);
            get_task_struct(p);
        }
        wake_up_new_task(p);
        /* forking complete and child started to run, tell ptracer */
        if (unlikely(trace))
            ptrace_event_pid(trace, pid);
        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);
    }
    return nr;
}

您已经正确识别了如何使用return nr返回新的过程ID。但是,由于此代码是在父线程上执行的,因此您永远不会在任何地方看到return 0。此代码不适合创建的新过程。

现在让我们检查_do_fork功能。

...
}
p = copy_process(clone_flags, stack_start, stack_size,
         child_tidptr, NULL, trace, tls, NUMA_NO_NODE);
add_latent_entropy();
...

这是所有魔术发生的地方。当您致电copy_process时,它在内部调用copy_thread,这是目标特定代码。此功能负责应对线程相关的数据结构。

现在说我们将目标作为x86_64,并带有调用约定,即返回值在%rax寄存器中返回。然后,此功能将0复制到%rax中,并将return_from_fork地址的值复制到%rip(指令指针(。

在其他平台上,ABI可能需要返回值才能进入堆栈。在这种情况下,0放在堆栈上。copy_thread是特定于目标的,但copy_process不是。

这是针对X86_64的copy_thread的实现。您可以在第160行中看到SP寄存器正在设置。在第182行中,您可以看到%ax(是%rax的子注册者(设置为0。

我希望这会消除一些混乱。

相关内容

最新更新