简单的问题,但我还没有找到答案。给定一个特定的PID,我可以确定该过程是否有效?我正在研究一个C程序,这让我疯狂。我读到了kill(pid,0)
可以解决问题的地方,但无论进程是否正在运行(或者看起来如此),都返回0。
任何提示?
其他信息:
我感兴趣的过程是由fork()
发起的孩子。
子进程应该在到达语句exit(0).
时终止。至少这是我的预期......显然它没有。
更多信息:
使用fork()
创建的子进程执行系统命令,该命令可能因最终用户而异。整个过程是批处理过程的一部分,因此没有机会进入并修复某些东西。此子进程可能必须执行的任务之一是建立与远程服务器的连接,以便在那里存储一些文档。这可能是另一台Linux机器,也可能是Win Server(或其他可能的东西)。出于这个原因,我不想等待子进程。我希望父母等待一段特定的时间(例如10秒)然后杀死子进程,如果它还没有完成的话。出于同样的原因,如果孩子在3毫秒内完成任务,我不希望父进程等待10秒。
我似乎不是第一个遇到这个问题的人。
答案 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
并设置正确的errno
(ESRCH
)。如果你分叉一个进程,而父亲存在,那么它有责任发出wait()
来获取他们的孩子的终止信息。如果没有,孩子们会四处游荡,直到父亲去世。
想确定吗?弄清楚孩子(据称)僵尸的pid
并发出此命令:
cat /proc/[pid]/status | grep "State"
它应该为僵尸(Z
)显示man 5 proc
。
希望这有帮助!
答案 2 :(得分:1)
如果我理解这个问题 - 现在对所有评论都有点困惑 - 解决方案非常简单。
在父级中建立信号处理程序。 SIGCHLD
的默认值是忽略它,但通过设置处理程序,信号将在子项完成时传递给父项。完成后,请使用wait
或waitpid
,以适合您的需求为准。您不必以这种方式wait
或重复投票(waitpid
)。
设置计时器(例如itimer
,timer_create
,alarm
等。如果计时器在孩子完成之前熄灭,kill
它。如果孩子先完成,请关闭计时器。有明显的(但不可避免的)竞争条件,但没有什么特别复杂的处理。
答案 3 :(得分:0)
我发现了很多关于fork()和信号的信息。我现在能够提供解决问题的样本。这段代码中有一些额外的东西可以忽略(比如毫秒的东西)。为了理解它的作用,子进程中的信号处理程序,全局布尔stopOnSignal
和kill()
命令是必不可少的方面。请注意,在这种情况下,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