我们应该在子进程中使用退出还是返回

Posted

技术标签:

【中文标题】我们应该在子进程中使用退出还是返回【英文标题】:Should we use exit or return in child process 【发布时间】:2019-05-11 12:24:10 【问题描述】:

说我用fork创建了一个子进程。这是一个例子:

pid_t pid=fork();
if (pid==0) /* child */

    // do something
    exit(0); // _exit, exit or return????

else /* parrent */

    wait(nullptr);
    return 0;

我见过很多fork 的例子。其中一些使用_exit 终止子进程以避免刷新I/O 缓冲区,其他使用exit 终止子进程。但他们都没有使用return。据我了解,_exitexit 不会自动调用析构函数,那么在子进程中调用return 而不是exit 会更好吗?还是因为我见过的所有示例都是 C,而不是 C++,所以它们不需要担心析构函数?

【问题讨论】:

有些人认为从main 返回是正常程序终止的唯一形式,而在其他任何地方退出或中止它被认为是异常终止。这当然是因为它很难推理程序控制流或跟踪未初始化的顺序是否正确。请注意,C 中没有析构函数,但这意味着您必须担心 10 倍以上,因为您需要手动执行所有取消初始化。 在 C 中,通过 return x; 或调用 exit(x) 离开 main() 没有区别。 在 C 中,除了在 main() returning 从函数或 exit()ing 中执行此操作之外,过程确实不同的。 @alk 但在 C++ 中,从 main() 返回或调用 exit(3) 确实不同,正如 OP 正确注意到的那样。但它是在 main() 中定义的自动变量的析构函数,在 exit(3) 的情况下不会被调用,而不是静态/全局变量,无论程序是从 main() 返回还是调用,它们的析构函数都会被调用明确退出(3)。 出于好奇,您需要调用哪些析构函数来退出进程清理尚未处理其工作的析构函数?每当一个进程退出时,它的所有内存都会被释放,并且它的所有文件描述符都会为你关闭,即使你泄漏了内存或让文件保持打开状态。 【参考方案1】:

您可以使用_exitexit,但不应使用return。当你派生一个孩子时,你会保留整个调用堆栈作为派生孩子的一部分。因此,如果您使用return,您最终会一直返回到您的程序,可能会继续执行其他任务,这几乎肯定不是您想要的。

例如,如果你有这样的 sn-p:

int get_value()

    pid_t pid;
    if (!(pid = fork())) 
        int x = 0;
        // do something with x.
        exit(x);
    
    else 
        int status;
        wait(&status);
        return status;
    


int main()

    int value = get_value();
    switch (get_value()) 
        case 0:
            // call f
            break;
        case 255 << 8:
            // call g
            break;
    

您最终可能会致电fg 或使用return 进行其他工作,这绝对是不希望的。

如果你调用_exit,注册到atexit 的函数不会被调用。这是在线程环境中正确的做法。如果您不是在线程环境中工作,并且您没有使用atexit 注册任何处理程序,那么它们应该在功能上等效。

如果您希望调用子进程中的析构函数,请将子进程代码放在其自己的函数中,并让其变量在超出范围时自动销毁。 exit 不会为您销毁对象,这很好,因为通常您不想在子进程中销毁在父进程中创建的对象。

【讨论】:

【参考方案2】:

如果你正在寻找子进程的退出代码,你可以使用 return,只是说进程运行和执行正确/不正确。与在程序中处理 main 函数相同。否则,只需使用 exit 停止进程继续运行。

【讨论】:

请看this comment。【参考方案3】:

在任何情况下都应避免使用退出命令,除非结束程序的执行。对于其他任何事情,我都会使用 return。

【讨论】:

【参考方案4】:

fork 会复制整个进程,并不等同于启动一个新的 main 函数的线程。 返回将简单地从当前函数返回,子函数将在封闭函数中继续执行。

因此,在您 sn-p 中,您必须终止孩子,否则它将“逃脱”。您可以通过调用exit()std::terminate() 来实现。在这两种情况下都没有调用析构函数。不要混合使用两种不同的语言。

如果你真的需要在子进程中调用析构函数,抛出一个异常并在 main 中捕获它。这将正确展开堆栈。

【讨论】:

以上是关于我们应该在子进程中使用退出还是返回的主要内容,如果未能解决你的问题,请参考以下文章

我啥时候应该在子进程中使用`wait`而不是`communicate`?

使用 c 安排警报

fork和exec函数

Nodejs 进程信号

C库函数和系统调用的区别

fork进程与Threading之超时退出