在 C 中等待子进程终止的最佳实践

Posted

技术标签:

【中文标题】在 C 中等待子进程终止的最佳实践【英文标题】:Best practice for waiting for a child process termination in C 【发布时间】:2021-12-14 06:15:47 【问题描述】:

我正在编写一个 C 库,它会在某个时候分叉另一个进程,然后等待其完成。

我想以最稳健和通用的方式编写等待子进程完成的代码,以处理所有可能的情况,例如调用进程产生其他子进程、接收信号等。

以下 C 代码是否正确使用waitpid,即以最稳健的方式?

void waitForChildProcess(int child_pid) 
    int rc, err;
    do 
        //waiting only for my own child and only for its termination.
        //The status value is irrelevant (I think) because option '0' should mean 
        //to only wait for a child termination event 
        // and I don't care about the child's exit code:
        rc = waitpid(child_pid, NULL, 0);
        err = errno;
     while (rc == -1 && err == EINTR); //ignoring a signal

【问题讨论】:

如果你在生成子进程后什么都不做,fork() 不是更容易吗? @Matthieu fork() 是产生子进程的函数。父进程获取子进程 PID 的副本,OP 希望使用该信息等待子进程终止。 【参考方案1】:

是的,waitpid(child_pid, ...) 是最稳健的方式。

如果子进程已经退出,它将返回 child_pid,如果发生错误,则返回 -1 并设置errnoECHILD 如果子进程不存在(从未创建或已被收割)或不是此进程的子进程,EINVAL 如果选项(第三个参数)具有无效值,或者 EINTR 如果信号被传递到未安装 SA_RESTART 标志的信号处理程序),或者0 如果WNOHANG选项(第三个参数)已指定,子进程尚未退出。

不过,我建议稍作改动:

/* Wait for child process to exit.
 * @child_pid   Process ID of the child process
 * @status      Pointer to where the child status
 *              is stored; may be NULL
 * @return       0  if success
 *              -1  if an error occurs, see errno.
*/
int waitForChildProcess(pid_t child_pid, int *status)

    int rc;

    if (child_pid <= 1) 
        errno = EINVAL;
        return -1;
    

    do 
        rc = waipid(child_pid, status, 0);
     while (rc == -1 && errno == EINTR);
    if (rc == child_pid)
        return 0;

    /* This should not happen, but let's be careful. */
    if (rc != -1)
        errno = ECHILD;

    return -1;

在 Linux 和 POSIXy 系统中,进程 ID 是正整数。正如您在man 2 waitpid 手册页中所见,零和负PID 指的是进程组,而-1 指的是任何子进程。进程1特殊,init;它永远不会退出并设置其余的用户空间。因此,当前进程的子进程可以拥有的最小 PID 是 2。

我认为为这些使用正确的类型是明智的:pid_t 用于进程 ID,例如 size_t 用于对象的内存大小(包括 strlen() 的返回值。)

提供status 指针(以便调用者可以使用WIFEXITED()+WEXITSTATUS()WIFSIGNALED()+WTERMSIG() 进行检查)很方便,因为任何对它不感兴趣的调用者都可以提供@987654339 @。 (NULL 明确允许用于 wait()waitpid() 的状态指针。)

从技术上讲,options==0waitpid() 应该只返回子 PID 或 -1(设置 errno)。但是,由于检查非常便宜,我更愿意将其他所有内容都视为 ECHILD 错误,因为这样会得到最可靠的结果。

调用者可以随意忽略返回值。但是,如果他们想知道,如果成功则返回值为 0,否则为 -1 并设置 errno(并且 strerror(errno) 提供了文本原因)。

【讨论】:

我没有'-1;你的答案,但我看不出额外的代码行如何带来更多的健壮性。例如,在 while 循环之后,'rc' 怎么可能是 child_pid 或 -1 以外的任何值?

以上是关于在 C 中等待子进程终止的最佳实践的主要内容,如果未能解决你的问题,请参考以下文章

任务终止的最佳实践

在 webapi 中使用异步等待的最佳实践

AWS EC2-Instance 备份/终止/启动的最佳实践是啥

DLL 注入的最佳实践?

等待模态显示时进度条的最佳实践

等待多个线程时的最佳实践 - 时间、计数还是其他?