Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/unix/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/flutter/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 叉子和返回两次_C_Unix_Fork_Freebsd_Openbsd - Fatal编程技术网

C 叉子和返回两次

C 叉子和返回两次,c,unix,fork,freebsd,openbsd,C,Unix,Fork,Freebsd,Openbsd,我正在从事一个需要在unix中实现fork()的项目。我读过freeBSD和openBSD的源代码,但很难理解。有人能解释一下这个概念吗?我知道一个返回是子进程的pid,返回给父进程,另一个返回为零,返回给子进程。但我无法理解如何实现返回两次的想法。。。我怎么能回来两次?提前感谢大家。当您调用fork时,它会返回“两次”,因为fork生成两个进程,每个进程都返回 因此,如果要实现fork,则必须创建第二个进程而不结束第一个进程。然后,返回两次的行为将自然发生:两个不同的进程中的每一个都将继续执行

我正在从事一个需要在unix中实现fork()的项目。我读过freeBSD和openBSD的源代码,但很难理解。有人能解释一下这个概念吗?我知道一个返回是子进程的pid,返回给父进程,另一个返回为零,返回给子进程。但我无法理解如何实现返回两次的想法。。。我怎么能回来两次?提前感谢大家。

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


因此,如果要实现
fork
,则必须创建第二个进程而不结束第一个进程。然后,返回两次的行为将自然发生:两个不同的进程中的每一个都将继续执行,只是它们返回的值不同(子进程返回零,父进程返回子进程的PID)。

当您想到函数返回时,您会想到从入口点开始的常规代码流(通常是
main
),然后以严格确定的线性方式逐行执行

然而,在现实世界中,有可能有多个执行上下文,每个执行上下文都有自己的控制流(新的C++标准实际上包含了这个概念)。每个单独的进程是从<代码>主< /COD>开始的执行上下文,但也可以从现有的一个创建一个新的执行上下文。(事实上,所有操作系统都必须能够做到这一点!).

fork
是创建新执行上下文的一种方法,新上下文的入口点是
fork
返回的点。但是,原始上下文也会继续运行,并且在调用
fork
后会像往常一样继续运行。新上下文是一个单独的进程,因此
fork
返回(一次)在这两种情况下


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

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

首先,假设我们有下面的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()的作用是,它获取存储在堆栈上的返回地址并将其保存到一个局部变量。之后,它返回1。这是一个非零返回,它将我们带到第一个printf

在printf()之后,我们调用jmpback()。这是实际的hack。此函数使saveesp()再次返回

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

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

此外,我们实际上在挂起/恢复代码路径上的OpenBSD内核中使用了它。
看一看第231行和第250行,这与上面的C代码几乎相同。然后看一看汇编代码,第542行是savecpu函数,它在挂起时第一次返回,第375行是我们在恢复时第二次返回的位置。

它并不是真正的“返回两次”;它在两个进程中各返回一次。为什么需要实现
fork()
?您所在的系统还没有它吗?
#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