叉并返回两次

时间:2012-03-04 00:07:35

标签: c unix fork freebsd openbsd

我正在开发一个需要在unix中实现fork()的项目。我阅读了freeBSD和openBSD源代码,但实际上很难理解。有人可以解释回归两次的概念吗?我知道一个返回是一个子进程的pid,并且返回到父进程,而另一个返回到零并且它返回到子进程。但我无法理解如何实现这种两次返回的概念......我怎么能回来两次呢?提前谢谢大家。

3 个答案:

答案 0 :(得分:2)

当你调用fork时,它会返回“两次”,因为fork会生成两个进程,每个进程都会返回。

因此,如果您正在实施fork,则必须创建第二个流程而不结束第一个流程。然后返回两次行为将自然发生:两个不同进程中的每一个都将继续执行,只有它们返回的不同(子项给零,父项给出子项的PID)。 / p>

答案 1 :(得分:1)

当你想到一个函数返回时,你会考虑通常的代码流,它从入口点(通常是main)开始,然后以严格确定和线性的方式逐行执行。 / p>

但是,在实际系统中,可以有多个执行上下文,每个执行上下文都有自己的控制流(新的C ++标准实际上包含了这个概念)。每个单独的进程都是从main开始的执行上下文,但您也可以从现有进程中创建新的执行上下文(实际上,所有操作系统都必须能够执行此操作!)。 fork是创建新执行上下文的一种方法,新上下文的入口点是 fork返回的点。但是,原始上下文也会继续运行,并且会在fork调用后继续照常运行。新上下文是一个单独的进程,因此fork在两个上下文中都返回(一次)。

还有其他创建新执行上下文的方法;一种是通过实例化std::thread对象或使用特定于平台的函数来创建新的线程(在同一进程中);另一个是Linux的clone()函数,它是Linux中Posix线程实现和fork的基础(通过为内核的调度程序创建新的执行路径,以及是否复制所有虚拟内存(新进程))(新线程。)

答案 2 :(得分:0)

下面我将尝试解释如何从函数返回两次。 我从一开始就警告你,这一切都是黑客攻击。 但是有很多地方都在使用这些黑客。

首先让我们说我们有以下C程序。

#include <stdio.h>

uint64_t saved_ret;

int main(int argc, char *argv[])
{
        if (saveesp()) {
                printf("here! esp = %llX\n", saved_ret);
                jmpback();
        } else {
                printf("there! esp = %llX\n", saved_ret);
        }

        return 0;
}

现在我们想要saveesp()返回两次,以便我们可以同时访问两个printf。 所以这里是saveesp()的实现方式:

#define _ENTRY(x) \
        .text; .globl x; .type x,@function; x:
#define NENTRY(y)       _ENTRY(y)

NENTRY(saveesp)
        movq    (%rsp), %rax
        movq    %rax, saved_ret

        movl    $1, %eax
        ret

NENTRY(jmpback)
        xorq    %rax, %rax
        pushq   saved_ret
        ret

这绝不是可移植代码。但是您可以为要支持的所有平台编写类似的程序集存根。

saveesp()的作用是,它将存储在堆栈中的返回地址保存到本地变量中。然后它返回1.这是一个非零返回,它将我们带到第一个printf。

在printf()之后我们调用jmpback()。这是实际的黑客攻击。此函数使得看起来saveesp()第二次返回。

它通过将保存的返回地址按下堆栈并执行ret来完成此操作。 ret将从堆栈中弹出地址并跳转到它。这次返回代码设置为零。因此,当我们“回到”我们的C例程时,看起来我们刚从saveesp()返回,返回值为零。因此到达第二个printf。

如果你对这种黑客感兴趣,你应该阅读更多关于用于实现异常处理的C标准中的setjmp和longjmp。

另外,我们实际上在openBSD内核的suspend / resume代码路径中使用它。 在第231和250行查看here它与上面几乎相同的C代码。然后看看第542行的汇编代码here是savecpu函数,它返回第一次暂停时,第375行是我们返回第二次恢复时的情况。