【问题标题】:How can _do_fork() return two different PIDs (one for the parent process and one for the child process)_do_fork() 如何返回两个不同的 PID(一个用于父进程,一个用于子进程)
【发布时间】:2017-11-29 23:36:29
【问题描述】:

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

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

答案How does fork() know when to return 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;
}

【问题讨论】:

  • 你想看懂linux的代码,有人试过了,还是有问题。

标签: c linux process linux-kernel fork


【解决方案1】:

您已经正确识别了新进程 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();
...

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

现在假设我们的目标是 X86_64,调用约定是在%rax 寄存器中返回返回值。然后,此函数将0 复制到%rax 并将return_from_fork 地址的值复制到%rip(指令指针)。

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

This 是 X86_64 的 copy_thread 的实现。您可以在第 160 行附近看到正在设置的 sp 寄存器。在第 182 行,您可以看到 %ax(它是 %rax 的子寄存器)被设置为 0。

我希望这能消除一些困惑。

【讨论】:

    猜你喜欢
    • 2019-03-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-04
    • 2012-10-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多