如何编写一个程序,告诉我的其他程序什么时候结束?
答案 0 :(得分:11)
对不是由你自己生成的程序执行waitpid()或waitid()的唯一方法是通过ptrace来实现它的父级。
以下是如何在posix操作系统上使用ptrace临时成为另一个进程父进程,然后等待该程序退出的示例。作为副作用,您还可以获得退出代码以及导致该程序退出的信号。:
#include <sys/ptrace.h>
#include <errno.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/wait.h>
int main(int argc, char** argv) {
int pid = atoi(argv[1]);
int status;
siginfo_t si;
switch (ptrace(PTRACE_ATTACH, pid, NULL)) {
case 0:
break;
case -ESRCH:
case -EPERM:
return 0;
default:
fprintf(stderr, "Failed to attach child\n");
return 1;
}
if (pid != wait(&status)) {
fprintf(stderr, "wrong wait signal\n");
return 1;
}
if (!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGSTOP)) {
/* The pid might not be running */
if (!kill(pid, 0)) {
fprintf(stderr, "SIGSTOP didn't stop child\n");
return 1;
} else {
return 0;
}
}
if (ptrace(PTRACE_CONT, pid, 0, 0)) {
fprintf(stderr, "Failed to restart child\n");
return 1;
}
while (1) {
if (waitid(P_PID, pid, &si, WSTOPPED | WEXITED)) {
// an error occurred.
if (errno == ECHILD)
return 0;
return 1;
}
errno = 0;
if (si.si_code & (CLD_STOPPED | CLD_TRAPPED)) {
/* If the child gets stopped, we have to PTRACE_CONT it
* this will happen when the child has a child that exits.
**/
if (ptrace(PTRACE_CONT, pid, 1, si.si_status)) {
if (errno == ENOSYS) {
/* Wow, we're stuffed. Stop and return */
return 0;
}
}
continue;
}
if (si.si_code & (CLD_EXITED | CLD_KILLED | CLD_DUMPED)) {
return si.si_status;
}
// Fall through to exiting.
return 1;
}
}
答案 1 :(得分:4)
在Windows上,我使用的一种技术是创建一个全局命名对象(例如带有CreateMutex的互斥锁),然后让监视程序打开相同的名为mutex并等待它(使用WaitForSingleObject)。第一个程序退出后,第二个程序就会获得互斥锁并知道第一个程序已退出。
在Unix上,解决此问题的通常方法是让第一个程序将其pid(getpid())写入文件。第二个程序可以监视这个pid(使用kill(pid,0))来查看第一个程序是否已经消失。这种方法受竞争条件的制约,无疑有更好的解决方法。
答案 2 :(得分:4)
如果你想生成另一个进程,然后在它运行时什么也不做,那么大多数高级语言已经有内置插件来执行此操作。例如,在Perl中,有system
和反引号用于运行进程并等待它们完成,而IPC::System::Simple等模块用于更容易地计算程序如何终止,以及是否是发生这种情况感到高兴或悲伤。使用为您处理一切的语言功能方式比自己尝试更轻松。
如果您使用的是Unix风格的系统,那么您分叉的进程终止将生成SIGCHLD信号。这意味着您的程序可以执行您的子进程正在运行的其他操作。
捕获SIGCHLD信号取决于您的语言。在Perl中,您设置了一个信号处理程序,如下所示:
use POSIX qw(:sys_wait_h);
sub child_handler {
while ((my $child = waitpid(-1, WNOHANG)) > 0) {
# We've caught a process dying, its PID is now in $child.
# The exit value and other information is in $?
}
$SIG{CHLD} \&child_handler; # SysV systems clear handlers when called,
# so we need to re-instate it.
}
# This establishes our handler.
$SIG{CHLD} = \&child_handler;
几乎可以肯定,CPAN上的模块比上面的示例代码做得更好。您可以将waitpid
与特定进程ID一起使用(而不是对所有进程使用-1),如果您希望让程序休眠直到另一个进程完成,则可以使用WNOHANG
。
请注意,当您进入信号处理程序时,会发生各种奇怪的事情。可能会有另一个信号进入(因此我们使用while循环来捕获所有死进程),并且根据您的语言,您可能会在另一个操作中处于分离状态!
如果您在Windows上使用Perl,则可以使用Win32::Process模块生成进程,并在生成的对象上调用->Wait
以等待它死亡。我不熟悉Win32::Process
的所有内容,但您应该能够等待0
(或1
一毫秒)的长度来检查是否有过程已经死了。
在其他语言和环境中,您的里程可能会有所不同。请确保当您的其他进程死亡时,请检查 它是如何死亡的。让一个子进程死掉,因为用户杀了它通常需要一个不同于它退出的响应,因为它成功地完成了它的任务。
一切顺利,
保
答案 3 :(得分:2)
你在Windows上吗?如果是这样,以下应解决问题 - 您需要传递进程ID:
bool WaitForProcessExit( DWORD _dwPID )
{
HANDLE hProc = NULL;
bool bReturn = false;
hProc = OpenProcess(SYNCHRONIZE, FALSE, _dwPID);
if(hProc != NULL)
{
if ( WAIT_OBJECT_0 == WaitForSingleObject(hProc, INFINITE) )
{
bReturn = true;
}
}
CloseHandle(hProc) ;
}
return bReturn;
}
注意:这是一个阻止功能。如果你想要非阻塞,那么你需要将INFINITE更改为一个较小的值并在循环中调用它(可能保持hProc句柄打开以避免在同一个PID的不同进程上重新打开)。
另外,我没有时间测试这段源代码,但是我从我的应用程序中提取了它。它可以工作。
答案 4 :(得分:0)
大多数操作系统通常都是同类的东西......
您记录有问题的程序的进程ID,并通过定期查询活动进程来监视它
至少在Windows中,您可以触发事件来执行此操作...
答案 5 :(得分:-2)
嗯,你不能,鉴于它的本质,这是一项不可能完成的任务。
假设您有一个程序foo,它将另一个程序foo-sub作为输入。
Foo {
func Stops(foo_sub) { run foo_sub; return 1; }
}
这一切的问题都是相当简单的设计,如果foo-sub是一个永不结束的程序,foo本身永远不会结束。没有办法从外面告诉foo-sub或foo是导致程序停止的原因,是什么决定你的程序是否需要一个世纪才能运行?
基本上这是计算机无法回答的问题之一。有关更完整的概述,维基百科有一篇关于this的文章。
答案 6 :(得分:-3)
这称为“暂停问题”,无法解决。 见http://en.wikipedia.org/wiki/Halting_problem
答案 7 :(得分:-3)
如果你想分析一个没有执行的程序而不是无法解决的问题。