我如何编写一个程序来告诉我的其他程序何时结束?
8 回答
在不是由您自己生成的程序上执行 waitpid() 或 waitid() 的唯一方法是通过 ptrace'ing 成为它的父级。
这是一个示例,说明如何在 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;
}
}
在 Windows 上,我使用的一种技术是创建一个全局命名对象(例如使用 CreateMutex 的互斥锁),然后让监控程序打开同名的互斥锁并等待它(使用 WaitForSingleObject)。第一个程序一退出,第二个程序就获得互斥体并知道第一个程序退出。
在 Unix 上,解决这个问题的常用方法是让第一个程序将其 pid (getpid()) 写入文件。第二个程序可以监视这个 pid(使用 kill(pid, 0))来查看第一个程序是否已经消失。这种方法受制于竞争条件,无疑有更好的方法来解决它。
如果您想生成另一个进程,然后在它运行时什么都不做,那么大多数高级语言已经内置了执行此操作的方法。例如,在 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
一毫秒)来检查进程是否已经死亡。
在其他语言和环境中,您的里程可能会有所不同。请确保当您的其他进程死亡时,您检查它是如何死亡的。由于用户杀死它而导致子进程死亡通常需要与它成功完成任务而退出不同的响应。
一切顺利,
保罗
你在 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 的不同进程上重新打开)。
另外,我没有时间测试这段源代码,但我从我的一个可以工作的应用程序中提取了它。
大多数操作系统通常是同一类的东西....
您记录相关程序的进程 ID,并通过定期查询活动进程来监控它
至少在 Windows 中,您可以触发事件来执行此操作...
嗯,你不能,鉴于它的性质,这是一项不可能完成的任务。
假设您有一个程序 foo,它将另一个程序 foo-sub 作为输入。
Foo {
func Stops(foo_sub) { run foo_sub; return 1; }
}
这一切的问题都是相当简单的设计,如果 foo-sub 是一个永远不会结束的程序,那么 foo 本身就永远不会结束。没有办法从外部判断是 foo-sub 还是 foo 是导致程序停止的原因,以及是什么决定了您的程序是否只需要一个世纪才能运行?
本质上,这是计算机无法回答的问题之一。要获得更完整的概述,维基百科有一篇关于此的文章。
这被称为“停机问题”并且无法解决。见http://en.wikipedia.org/wiki/Halting_problem
如果您想在不执行的情况下分析一个程序,那将是无法解决的问题。