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

Xap*_*ius 8 c linux fork process linux-kernel

我正在看_do_fork()函数()试图理解如何fork()返回父进程的子PID和子进程的0.

我认为它nr包含子进程的PID(将返回到调用者进程),但我看不出它如何能够返回0到子进程.

答案fork()如何知道何时返回0?说返回值是在为新进程创建的堆栈上传递的,但是(除了没有真正理解它)我在代码中找不到它.

那么,为子进程设置返回值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;
}
Run Code Online (Sandbox Code Playgroud)

Aja*_*iya 9

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

现在让我们来看看这个_do_fork功能.

...
}
p = copy_process(clone_flags, stack_start, stack_size,
         child_tidptr, NULL, trace, tls, NUMA_NO_NODE);
add_latent_entropy();
...
Run Code Online (Sandbox Code Playgroud)

这就是所有魔法发生的地方.当您调用copy_process时 ,它会在内部调用copy_thread哪个是特定于目标的代码.该函数负责处理与线程相关的数据结构.

现在说我们将目标作为X86_64与调用约定,返回值在%rax寄存器中返回.然后,该函数拷贝0到%rax和拷贝的值return_from_fork地址%rip(指令指针).

在其他平台上,ABI可能要求返回值进入堆栈.在那种情况下0放在堆栈上.copy_thread是特定目标但copy_process不是.

这是copy_threadX86_64 的实现.您可以在第160行周围看到正在设置的sp寄存器.在第182行,您可以看到%ax(%rax的子寄存器)被设置为0.

我希望这可以解决一些困惑.