获取特定PID的状态

时间:2014-01-08 15:05:48

标签: c linux pid

简单的问题,但我还没有找到答案。给定一个特定的PID,我可以确定该过程是否有效?我正在研究一个C程序,这让我疯狂。我读到了kill(pid,0)可以解决问题的地方,但无论进程是否正在运行(或者看起来如此),都返回0。

任何提示?

其他信息: 我感兴趣的过程是由fork()发起的孩子。 子进程应该在到达语句exit(0).时终止。至少这是我的预期......显然它没有。

更多信息: 使用fork()创建的子进程执行系统命令,该命令可能因最终用户而异。整个过程是批处理过程的一部分,因此没有机会进入并修复某些东西。此子进程可能必须执行的任务之一是建立与远程服务器的连接,以便在那里存储一些文档。这可能是另一台Linux机器,也可能是Win Server(或其他可能的东西)。出于这个原因,我不想等待子进程。我希望父母等待一段特定的时间(例如10秒)然后杀死子进程,如果它还没有完成的话。出于同样的原因,如果孩子在3毫秒内完成任务,我不希望父进程等待10秒。

我似乎不是第一个遇到这个问题的人。

5 个答案:

答案 0 :(得分:5)

您正在寻找waitpid,它将返回给定PID的状态信息。

对于不相关的进程,您可以在linux中使用/proc/[pid]/stat并读取输出。

关于更新的信息

IMO有两种情况。

首先:

子进程快速完成。使用waitpid(使用WNOHANG)并获取它的状态,然后你知道它是如何终止的并且它实际上已经终止。

第二

子进程正在运行。将waitpid与WNOHANG一起使用并检查它是否仍在运行。如果没有做父母需要做的其他事情,经过足够的时间,孩子仍然跑步,你可以杀死它,或做任何你的设计认为合适的反应。

无论哪种方式,waitpid都是您需要的。伪代码只是证明,你可以在其间做其他事情,即使孩子提前终止你也不需要等待10秒,因为这样的民意调查并不合适。

伪码:

 pid_t pid;
 pid = fork();
 while(1)
 {
     if(pid == 0)
     {
         if(status = waitpid(pid, WNOHANG))
         {
             if(status != exited)
             {
                 if(checkExpiryTime() == true)
                    kill(pid, SIGKILL);
                 else
                   sleep(x); // or whatever is appropriate in your case.
             }
         }
     }
     else
     {
          // do childstuff here.
     }
 }

答案 1 :(得分:2)

Linux终止后不会删除进程描述符,因为父母以后可能需要他们的信息。当父进程发出wait()-like系统调用时,Linux只会完全删除它们。通常这是由它的父亲完成的,但是如果这个过程是孤儿,它就变成了init的孩子而init最终会发出wait()-like系统调用来杀死僵尸进程。

话虽如此,在父亲发出wait()-like调用之前,孩子的进程描述符仍然被分配EXIT_ZOMBIE状态。这就是kill(pid, 0)正常工作的原因。它能够使用pid字段找到进程描述符。

man 3 exit进一步扩展了这一点,并解释了与wait(2)和僵尸流程的关系。

关于kill(pid, 0)。它可用于确定进程是否存在。但它并没有告诉你是否正在运行或等待父进行wait()系统调用以从内核的内存中扫描它。

如果存在,kill()将返回0.如果不存在,则kill将返回-1并设置正确的errnoESRCH)。如果你分叉一个进程,而父亲存在,那么它有责任发出wait()来获取他们的孩子的终止信息。如果没有,孩子们会四处游荡,直到父亲去世。

想确定吗?弄清楚孩子(据称)僵尸的pid并发出此命令:

cat /proc/[pid]/status | grep "State"

它应该为僵尸(Z)显示man 5 proc

希望这有帮助!

答案 2 :(得分:1)

如果我理解这个问题 - 现在对所有评论都有点困惑 - 解决方案非常简单。

  1. 在父级中建立信号处理程序。 SIGCHLD的默认值是忽略它,但通过设置处理程序,信号将在子项完成时传递给父项。完成后,请使用waitwaitpid,以适合您的需求为准。您不必以这种方式wait或重复投票(waitpid)。

  2. 设置计时器(例如itimertimer_createalarm等。如果计时器在孩子完成之前熄灭,kill它。如果孩子先完成,请关闭计时器。有明显的(但不可避免的)竞争条件,但没有什么特别复杂的处理。

答案 3 :(得分:0)

我发现了很多关于fork()和信号的信息。我现在能够提供解决问题的样本。这段代码中有一些额外的东西可以忽略(比如毫秒的东西)。为了理解它的作用,子进程中的信号处理程序,全局布尔stopOnSignalkill()命令是必不可少的方面。请注意,在这种情况下,kill()只会向getppid().

标识的父级发送信号

所以这是我的样本(编辑为在10.01.2014上使用exec()):

#include <time.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
#include <bits/signum.h>

static bool stopOnSignal = false;



uint32_t clockedMilliseconds(clock_t t1, clock_t t2)
{
    if (t2 > t1) { return (t2 - t1) / (CLOCKS_PER_SEC/1000); }
    else /* the time has wrapped around since the values were set */
    { return t2 / (CLOCKS_PER_SEC/1000); }
}



void signalHandler(int signum)
{
   printf("Caught signal %d\n",signum);
   stopOnSignal = true;
}



int main (int argc, char *argv[])
{
    pid_t cpid;
    char * mstr;
    int rc = -999999;
    int krc = 0;
    uint32_t timeoutWait =  10000 ; // default 10 secs
    int count = 0;
    int loops = 0;

    signal(SIGUSR1, signalHandler);

    if (argc < 2) {
        printf("usage: ./sigparent sleep-milliseconds [timeout-milliseconds]");
        exit -1;
    }

    cpid = fork();
    if (cpid == -1) {
        printf("%d : failed to start child process.\n", errno);
        perror("fork");
        exit(-1);
    }

    if (cpid == 0) { /* Code executed by child process */

        execl("sleeping_child", argv[1],(char *) NULL);

    }
    else { /* Code executed by parent */

        if (argc > 2) sscanf(argv[2],"%d",&timeoutWait);
        clock_t t1 = clock();
        clock_t t2;

        do { /* loop until child process ends or timeout limit is reached */

            if (count < 100000) count++;
            else {
               loops++;
               printf("loops of 100000 duration = %d \n", loops);
               count = 0;
            }
            t2 = clock();

            if ( clockedMilliseconds(t1, t2) > timeoutWait) {
                krc = kill(cpid,9);
                rc = 3;
                break;
            }
            if ( stopOnSignal == true ) {
                //krc = kill(cpid,9);
                rc = 0;
                break;
            }
        } while (true);

        if (rc == -999999) {
                printf("process failed horribly!\n");
        }
        else if (rc == 3) {
            if (krc == 0){ /* child process timed out */
                printf("TIMEOUT, waiting %d ms on pid %d\n",
                       timeoutWait, cpid);
            }
            else { /* attempted timeout failed - result is unpredictable */
                printf("%d : attempted TIMEOUT failed.\n", errno);
                perror("kill");
            }
        }
        else { /* rc == 0 */
             printf("child process ended normally.\n");
        }
    }
    exit(0);
}

这可能不太好,但它可以作为超时子进程的有效方法。将此代码保存在文件中 - 比如sigparent.c。您还需要外部程序sleeping_child.c。

/* sleeping_child */
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>

int main (int argc, char * argv[]) {

    int rc = 0;
    int millis;

    if (argc > 2) sscanf(argv[2],"%d",&millis);
    else millis = 2000;

    rc = usleep(millis * 1000);
    printf("slept for %d milliseconds\n",millis);
    printf("parent is %d \n", getppid());
    kill(getppid(),SIGUSR1);
    return(rc);
}

不要试图单独运行sleeping_child,因为它会杀死你的bash会话。要试用它,请使用以下命令:

# to compile...
gcc -o sleeping_child sleeping_child.c
gcc -o sigparent sigparent.c
# to let the child terminate, set the second parameter to greater than the first...
./sigparent 1000 3000
# to cause the parent to timeout the child make the first parameter greater...
./sigparent 10000 3000

非常感谢Duck提供有关信号的暗示。然而,似乎有一种更优雅的方式来做到这一点,而不需要信号。来自同事的一个简单的示例程序给了我一个关于如何通过waitpid().实现我的目标的线索。当我开始工作时,我会发布解决方案。

答案 4 :(得分:0)

Achem,我是用尽可能最干净的方式做到的,但这是个主意。如果您想使用毫秒,可以使用itimer,或者更好,timer_create而不是alarm。如果你想扩展它以处理多个孩子(或在父母中做一些有用的事情)你也可以这样做。

#define _POSIX_C_SOURCE 1

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/types.h>

pid_t cpid;    
volatile sig_atomic_t done = 0;

void alarmHandler(int signum)
{
    if (kill(cpid, SIGTERM) != -1)
        printf("kill signal sent to child from parent\n");
    else
        if (errno == ESRCH)
            printf("kill could not find child, must already be dead\n");
        else
        {
            perror("kill");
            exit(EXIT_FAILURE);
        }
}

void childHandler(int signum)
{
    pid_t childpid;
    int status;

    while ((childpid = waitpid( -1, &status, WNOHANG)) > 0)
    {    
        if (WIFEXITED(status))
            printf("Child %d exited naturally\n", childpid);

        if (WIFSIGNALED(status))
            printf("Child %d exited because of signal\n", childpid);
    }

    if (childpid == -1 && errno != ECHILD)
    {
        perror("waitpid");
        exit(EXIT_FAILURE);
    }

    done = 1;
}

int main (int argc, char *argv[])
{
    int sleepSecs;
    int timeoutSecs;

    if (argc < 3)
    {
        printf("\nusage: %s sleep-seconds timeout-seconds\n\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    sscanf(argv[1], "%d", &sleepSecs);
    sscanf(argv[2], "%d", &timeoutSecs);

    signal(SIGCHLD, childHandler);
    signal(SIGALRM, alarmHandler);

    if ((cpid = fork()) == -1)
    {
        printf("%d : failed to start child process.\n", errno);
        perror("fork");
        exit( -1);
    }

    if (cpid == 0) //child
    {
        execl("./sleeping_child", "./sleeping_child", argv[1], (char *) NULL);

        perror("execl");
        exit(EXIT_FAILURE);
    }
    else //parent
    {
        alarm(timeoutSecs);

        while (! done)
        {
            sleep(1); // or do something useful instead
        }

        exit(0);
    }
}

儿童计划不需要做任何特别的事情就死了。

/* sleeping_child */
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

int main (int argc, char * argv[]) 
{
    printf("child will sleep for %s seconds\n", argv[1]);

    sleep(atoi(argv[1]));

    exit(0);
}

某些示例运行看起来像这样

$ simpleReap 3 1
child will sleep for 3 seconds
kill signal sent to child from parent
Child 5095 exited because of signal

$ simpleReap 1 3
child will sleep for 1 seconds
Child 5097 exited naturally