【问题标题】:notify parent process when child process dies当子进程死亡时通知父进程
【发布时间】:2014-02-13 18:11:16
【问题描述】:

假设我有一个父进程,它产生很少的工人(子进程)。 我想在其中一个子进程崩溃(被杀死)时得到通知。

获取此通知的最佳方式是什么。 我正在用 c 或 c++ 编程。 我在unix上。并使用 fork_and_exec 产生了 hte 子进程

【问题讨论】:

    标签: unix process parent-child pid notify


    【解决方案1】:

    如果您只想在子进程退出(正常或异常)时收到通知,您可以在父进程中编写一个处理程序来处理 SIGCHLD 信号。每当由此进程产生的进程死亡时,都会执行此处理程序。

    但是,您可以在此处理程序中执行的操作存在严重限制。如果收到其他信号,该处理程序将立即中断。即使变量赋值在信号处理程序内部也不是安全操作 - 赋值可能需要两条或更多机器指令,并且它可能被新信号中断,而值未定义。唯一可以在信号处理程序内部安全分配的变量是 sig_atomic_t 类型的变量(保证在一条指令中完成对这种类型变量的分配)。

    父进程也必须为子进程“wait”(使用等待功能)——否则,子进程在终止后将成为僵尸。

    在我的情况下,这个解决方案就足够了,因为我只需要减少 sig_atomic_t 类型的计数器变量。如果您需要在孩子死亡时进行一些“严肃”处理,则需要其他解决方案。思路可能如下:

    • 在主进程中,有一个专用线程将等待孩子的死亡(这包括从 main 成功返回、被 SIGTERM 和 SIGKILL 信号杀死以及任何其他异常终止)。
    • 为此,我们可以使用 UNIX waitid() 方法,该方法让我们指定我们正在等待的孩子、我们正在等待的事件类型(EXITED、STOPPED 或 CONTINUED)并在成功时给出我们有关终止进程的有用信息(例如孩子的 PID)。
    • 因此,在这个线程中,我们使用 waitid() 来等待孩子,每次孩子死后,这个函数都会终止,为我们提供有用的信息。
    • waitid() 返回后,我们应该检查它的返回值和 errno,看看是否一切正常。如果是,我们可以执行我们的清理程序(当孩子死亡时我们想要执行的代码块),然后我们继续监听更多的死亡事件(听起来很糟糕)。

    C++ 示例可以在下面找到 - 请注意我没有使用线程来等待孩子,但修改示例以使用线程应该不难。

    #include <iostream>
    #include <sys/types.h>
    #include <unistd.h>
    
    int spawn(char* process_name, char** arguments){
        pid_t child_id = fork();
    
        if (child_id != 0)
            return child_id;
        else{
            execvp(process_name, arguments);
            fprintf(stderr, "An error occured while executing new process.\n");
            abort();
        }
    }
    
    
    int main(int argc, const char * argv[])
    {
        char* arguments[] = {
            "/Path_to_Child_Exe/Child_Process",
            nullptr
        };
    
        int i=0;
        while (i++ < 10)
            spawn((char*) "/Path_to_Child_Exe/Child_Process", arguments);
    
    
        while (true){
            siginfo_t exit_info;
            int retVal = waitid(P_ALL, -1, &exit_info, WEXITED);
    
            if (retVal == -1 && errno == ECHILD){
                std::cout << "No more children.\n";
                break;
            }
    
            std::cout << "Child with PID " << exit_info.si_pid << " terminated.\n";
    
            sleep(1);
        }
    
        return 0;
    }
    

    还需要注意的是,我们在 while 循环的每次迭代结束时调用 sleep。在等待孩子的线程中也应该这样做。否则,如果我们在没有子进程时不暂停地调用 waitid() 函数,CPU 使用率会非常高(我猜是忙等待的一种形式)。因此我们必须时不时调用 sleep 来给 CPU 一个休息的机会。

    【讨论】: