使用C以编程方式检查Linux中的僵尸子进程

时间:2018-05-25 19:07:37

标签: c wait zombie-process

我在RedHat Linux中编写了一个简单的C程序,在调用execv后等待使用waitpid的子进程。

int main( int argc, char * argv[] )
{
    int pid;
    int status = 0;
    int wait_ret;

    const char * process_path = argv[1];

    if ( argc < 2 )
    {
        exit( EXIT_FAILURE );
    }

    pid = fork(); //spawn child process

    if ( 0 == pid ) //child
    {
        int ret = execv( process_path, &argv[1] );

        if ( ret )
        {
            printf( "execv failed: %s\n", strerror( errno ) );
        }

        exit( EXIT_SUCCESS );
    }

    //wait for the child to terminate
    wait_ret = waitpid( pid, &status, WUNTRACED );

    if ( -1 == wait_ret )
    {
        printf( "ERROR: Failed to wait for process termination\n" );
        exit( EXIT_FAILURE );
    }

    // ... handlers for child exit status ...

    return 0;
}

我正在使用它作为我正在运行的某些进程的简单监视器。

我的问题是,一个进程特别是在退出时没有被waitpid收获,而是在waitpid挂起时永远保持在Zombie状态。我不确定为什么waitpid一旦变成僵尸(可能是泄露的文件描述符或其他东西)就无法收获这个过程。

我可以使用WNOHANG标志并轮询孩子的stat proc文件以检查Zombie状态,但我更喜欢更优雅的解决方案。也许有一些函数我可以使用它来获取Zombie状态而不轮询这个文件?

有没有人知道waitpid的替代方案,当进程变为Zombie时会返回?

其他信息:

通过在其中一个主题中调用exit( EXIT_FAILURE);来关闭子进程。

cat /proc/<CHILD_PID>/stat(退出前):

1037(my_program)S 1035 58 58 0 -1 4194560 1309 0 22 0 445 1749 0 0 20 0 13 0 4399 22347776 1136 4294967295 3336716288 3338455332 3472776112 3472775232 3335760920 0 0 4 31850 4294967295 0 0 17 0 0 0 26 0 0 3338489412 3338507560 3338600448

cat /proc/<CHILD_PID>/stat(退出后):

1037(my_program)Z 1035 58 58 0 -1 4227340 1316 0 22 0 464 1834 0 0 20 0 2 0 4399 0 0 4294967295 0 0 0 0 0 0 0 4 31850 4294967295 0 0 17 0 0 0 26 0 0 0 0 0

请注意,在这种情况下,子PID为1037,父PID为1035。

2 个答案:

答案 0 :(得分:0)

我的问题是,一个进程特别是在退出时没有被waitpid收获,而是在waitpid被挂起时永远处于Zombie状态?如果我理解正确,你不需要孩子成为僵尸然后使用SA_NOCLDWAIT标志。来自sigaction()

的手册页
  

SA_NOCLDWAIT(自Linux 2.6起)                     如果signum是SIGCHLD,请不要将孩子变成僵尸                     当他们终止。另见waitpid(2)。这个标志是                     只有在为SIGCHLD建立处理程序时才有意义,或者                     将信号的处置设置为SIG_DFL时。

              If the SA_NOCLDWAIT flag is set when establishing a  handler
              for SIGCHLD, POSIX.1 leaves it unspecified whether a SIGCHLD
              signal is generated when a  child  process  terminates.   On
              Linux,  a  SIGCHLD signal is generated in this case; on some
              other implementations, it is not.

想法是当子进程首先完成,父进程收到17号信号或SIGCHLD&amp;当父母仍在运行时,子进程将变为僵尸。所以如何尽快删除子,它变成僵尸,解决方案是使用标志SA_NOCLDWAIT

以下是示例代码

void my_isr(int n) {
        /* error handling */
}
int main(void) {
        if(fork()==0) { /* child process */
                printf("In child process ..c_pid: %d and p_pid : %d\n",getpid(),getppid());
                sleep(5);
                printf("sleep over .. now exiting \n");
        }
        else { /*parent process */
                struct sigaction v;
                v.sa_handler=my_isr;/* SET THE HANDLER TO ISR */
                v.sa_flags=SA_NOCLDWAIT; /* it will not let child to become zombie */
                sigemptyset(&v.sa_mask);
                sigaction(17,&v,NULL);/* when parent receives SIGCHLD, IT GETS CALLED */
                while(1); /*for observation purpose, to make parent process alive */
        }
        return 0;
}

只需评论/取消注释v.sa_flags=SA_NOCLDWAIT;行&amp;通过在一个终端中运行a.out来分析行为。检查另一个终端中的ps -el | grep pts/0

有没有人知道waitpid的替代方法,当进程变为僵尸时会返回?使用WNOHANG就像你做的那样&amp;在waitpid()

的手册页中说明
  

如果孩子已经停止(但没有追踪),WUNTRACED也会返回   通过                      ptrace的(2))。已经停止的被追踪儿童的状况                      即使未指定此选项,也会提供。

答案 1 :(得分:0)

任何终止的进程都会变成僵尸进程,直到它被 wait 调用回收。在这里等待似乎并非在所有情况下都会发生。

从给出的代码中,我无法弄清楚为什么等待没有发生并且进程仍然是僵尸。 (无论如何也不能不运行它)

但不是只等待特定的 pid,您可以通过使用 -1 作为 waitpid 的第一个参数来等待 任何 子项。不要使用 WNOHANG,因为它需要忙轮询(不要这样做)。

您可能还想删除 WUNTRACED,除非您有特定的理由要包含它。但是丢掉它并看看它有什么不同没有坏处。