【问题标题】:Why does a process create a zombie if execv fails, but not if execv is successful and terminates?如果 exec 失败,为什么进程会创建僵尸,但如果 execve 成功并终止则不会?
【发布时间】:2019-11-27 13:09:24
【问题描述】:

所以我对我的 C 程序的行为感到困惑。我正在使用构造,

int pid = fork();
if (pid == 0) {
    if(file_upload_script_path) {
        rc = execv(file_upload_script_path, args);
        if(rc == -1) {   
            printf("Error has occured when starting file_upload.exp!\n");
            exit(0);
        }
    } else {   
        printf("Error with memory allocation!\n");
    }
}
else {
    printf("pid=%d\n", pid);
}

fork 进程并运行脚本以进行文件上传。该脚本将通过完成上传或失败自行安全终止。

现在,脚本路径有问题,导致execv 失败。这里我注意到如果 execv 完成,子进程将成功终止,但如果它失败(r==-1)并且我退出进程,它将成为僵尸。有谁知道为什么会这样?

请注意,我知道为什么子进程会变成僵尸。我感到困惑的是,如果 execv 工作,为什么进程不会变成僵尸。

编辑:

我有一个关于 errno 和错误原因的问题。错误的原因是已知的。构建过程出现问题,因此脚本的路径与预期不同。

但是,这种情况可能会再次发生,我想确保我的程序不会开始产生僵尸。在某些情况下创建僵尸而不在其他情况下创建僵尸的行为非常令人困惑。

BR 帕特里克

【问题讨论】:

  • 而errno的值是多少?
  • @KamilCuk 错误?你的意思是execv的错误代码吗?这不会是rc吗?或者更好的问题,使用 errno.h,这将如何实际解决问题?
  • 在子进程中 exec() 失败后调用 exit() 是不安全的。 exit() 所做的一件事是刷新所有缓冲的流,并且由于子进程是父进程的副本,并且包含在调用 fork() 时父进程缓冲的任何数据的副本,因此 child 进程随后会将这些数据刷新到 父级的 文件中。您需要调用 POSIX _exit() 或标准 C _Exit() 函数。
  • From man execv: The return value is -1, and errno is set to indicate the error. errno 设置为指示错误。哦,我看到了编辑。但是无论是否调用 execv 都必须收割孩子,而不是创建僵尸。

标签: c fork child-process


【解决方案1】:

如果您不想创建僵尸,您的程序必须获取僵尸进程,无论它们是否调用execv 或不调用它,或者无论execv 调用是否成功。要“自动”获取僵尸进程处理 SIGCHLD 信号:

void handle_sigchld(int sig) {
    int saved_errno = errno;
    while (waitpid((pid_t)(-1), 0, WNOHANG) > 0) {}
    errno = saved_errno;
}

int main() {
    signal(SIGCHLD, handle_sigchld);
    // rest of your program....
}

灵感来自:this link

或者你可能只想收获这个指定的孩子,因为稍后你想调用fork()并处理孩子的返回值。然后将返回的pid 从父级中的fork() 传递给信号处理程序,如果需要,将wait 传递给sigchld 中的这个pid(进行一些检查,例如,如果pid 已经完成,则忽略未来的SIGCHLD 等......) .

【讨论】:

  • 谢谢卡米尔。我会试试这个。但是,我希望它会起作用。该脚本可能会被调用多次,但它应该能够自行处理。所以基本上我不需要跟踪 pid。
  • 就像一个魅力:)。谢谢!
【解决方案2】:

在这种情况下,当 execv 失败时,子进程被杀死。我认为有趣的部分是当您调用 exec 系列函数时会发生什么。

exec 系列函数将进程的当前映像替换为您将要执行的二进制文件的新映像。

因此,无论 的代码都不会保留 - 并且脚本中的错误会导致其死亡。

在这里,父进程需要使用等待功能的函数(阅读:waitpid)来监听子进程的死亡。

当你说脚本有问题时,这意味着execv实际上已经成功创建了新的图像;但后者自己失败了。

这就是我认为正在发生的事情......

如果 if (rc==-1) 的 printf 正在执行,那么将 exit(0) 更改为 _exit(0) 可能会解决这个问题。

【讨论】:

  • if(rc == -1) 中的 printf 被执行了吗?
  • 是的,打印输出已执行。正如我所说,当 execv 失败时,父进程似乎在等待 sigterm 信号,但在它工作时却没有。如果脚本死了,这对我的应用程序来说不是问题。这是两个独立的行为管理自己。
  • 此外,当程序无法启动时,会出现僵尸。一般来说,我会假设所有子进程都会创建僵尸,但由于某种原因,当 execv 启动时没有。
  • 在这种情况下,我认为 _exit(0) 应该处理它而不是 exit (0);值得一试吗?
  • 实际上,当子进程死亡时,它会保留在进程表中,直到父进程收集到退出状态(geeksforgeeks.org/zombie-processes-prevention)。起初我不知道这一点,但现在知道了。关于 execv,似乎使用 execv 调用的进程以某种方式分离(因为如果父进程被杀死,它会继续运行)。
猜你喜欢
  • 1970-01-01
  • 2021-10-19
  • 2013-06-18
  • 1970-01-01
  • 2012-06-07
  • 2019-06-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多