Note: This is a blocking function. If you want non-blocking then you'll need to change the INFINITE to a smaller value and call it in a loop (probably keeping the hProc handle open to avoid reopening on a different process of the same PID).
Also, I've not had time to test this piece of source code, but I lifted it from an app of mine which does work.
The problem with this all be it rather simplistic design is that quite simply if foo-sub is a program that never ends, foo itself never ends. There is no way to tell from the outside if foo-sub or foo is what is causing the program to stop and what determines if your program simply takes a century to run?
Essentially this is one of the questions that a computer can't answer. For a more complete overview, Wikipedia has an article on this.
#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;
}
}
The only way to do a waitpid() or waitid() on a program that isn't spawned by yourself is to become its parent by ptrace'ing it.
Here is an example of how to use ptrace on a posix operating system to temporarily become another processes parent, and then wait until that program exits. As a side effect you can also get the exit code, and the signal that caused that program to exit.:
#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;
}
}
On Windows, a technique I've used is to create a global named object (such as a mutex with CreateMutex), and then have the monitoring program open that same named mutex and wait for it (with WaitForSingleObject). As soon as the first program exits, the second program obtains the mutex and knows that the first program exited.
On Unix, a usual way to solve this is to have the first program write its pid (getpid()) to a file. A second program can monitor this pid (using kill(pid, 0)) to see whether the first program is gone yet. This method is subject to race conditions and there are undoubtedly better ways to solve it.
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;
If you want to spawn another process, and then do nothing while it runs, then most higher-level languages already have built-ins for doing this. In Perl, for example, there's both system and backticks for running processes and waiting for them to finish, and modules such as IPC::System::Simple for making it easier to figure how the program terminated, and whether you're happy or sad about that having happened. Using a language feature that handles everything for you is way easier than trying to do it yourself.
If you're on a Unix-flavoured system, then the termination of a process that you've forked will generate a SIGCHLD signal. This means your program can do other things your child process is running.
Catching the SIGCHLD signal varies depending upon your language. In Perl, you set a signal handler like so:
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;
There's almost certainly modules on the CPAN that do a better job than the sample code above. You can use waitpid with a specific process ID (rather than -1 for all), and without WNOHANG if you want to have your program sleep until the other process has completed.
Be aware that while you're inside a signal handler, all sorts of weird things can happen. Another signal may come in (hence we use a while loop, to catch all dead processes), and depending upon your language, you may be part-way through another operation!
If you're using Perl on Windows, then you can use the Win32::Process module to spawn a process, and call ->Wait on the resulting object to wait for it to die. I'm not familiar with all the guts of Win32::Process, but you should be able to wait for a length of 0 (or 1 for a single millisecond) to check to see if a process is dead yet.
In other languages and environments, your mileage may vary. Please make sure that when your other process dies you check to see how it dies. Having a sub-process die because a user killed it usually requires a different response than it exiting because it successfully finished its task.
发布评论
评论(8)
您使用的是 Windows 吗? 如果是这样,以下应该可以解决问题 - 您需要传递进程 ID:
注意:这是一个阻塞函数。 如果你想要非阻塞,那么你需要将 INFINITE 更改为较小的值并在循环中调用它(可能保持 hProc 句柄打开以避免在同一 PID 的不同进程上重新打开)。
另外,我没有时间测试这段源代码,但我从我的一个确实有效的应用程序中提取了它。
Are you on Windows ? If so, the following should solve the problem - you need to pass the process ID:
Note: This is a blocking function. If you want non-blocking then you'll need to change the INFINITE to a smaller value and call it in a loop (probably keeping the hProc handle open to avoid reopening on a different process of the same PID).
Also, I've not had time to test this piece of source code, but I lifted it from an app of mine which does work.
这称为“停机问题”并且无法解决。
请参阅http://en.wikipedia.org/wiki/Halting_problem
This is called the "halting problem" and is not solvable.
See http://en.wikipedia.org/wiki/Halting_problem
如果你想分析一个不执行的程序,那么这是一个无法解决的问题。
If you want analyze one program without execution than it's unsolvable problem.
嗯,你不能,鉴于其性质,这是一项不可能完成的任务。
假设您有一个程序 foo,它接受另一个程序 foo-sub 作为输入。
这一切的问题是相当简单的设计,很简单,如果 foo-sub 是一个永远不会结束的程序,那么 foo 本身也永远不会结束。 没有办法从外部判断 foo-sub 或 foo 是否是导致程序停止的原因以及是什么决定了您的程序是否只需要一个世纪才能运行?
从本质上讲,这是计算机无法回答的问题之一。 如需更完整的概述,维基百科有一篇关于此内容的文章。
Umm you can't, this is an impossible task given the nature of it.
Let's say you have a program foo that takes as input another program foo-sub.
The problem with this all be it rather simplistic design is that quite simply if foo-sub is a program that never ends, foo itself never ends. There is no way to tell from the outside if foo-sub or foo is what is causing the program to stop and what determines if your program simply takes a century to run?
Essentially this is one of the questions that a computer can't answer. For a more complete overview, Wikipedia has an article on this.
大多数操作系统通常都是同一类事情....
您记录相关程序的进程ID,然后通过定期查询活动进程来监视它
至少在Windows中,您可以触发事件来执行此操作...
Most operating systems its generally the same kind of thing....
you record the process ID of the program in question and just monitor it by querying the actives processes periodically
In windows at least, you can trigger off events to do it...
对不是您自己生成的程序执行 waitpid() 或 waitid() 的唯一方法是通过 ptrace 使其成为其父级。
下面是如何在 posix 操作系统上使用 ptrace 暂时成为另一个进程父进程的示例,然后等待该程序退出。 作为副作用,您还可以获得退出代码以及导致该程序退出的信号。:
The only way to do a waitpid() or waitid() on a program that isn't spawned by yourself is to become its parent by ptrace'ing it.
Here is an example of how to use ptrace on a posix operating system to temporarily become another processes parent, and then wait until that program exits. As a side effect you can also get the exit code, and the signal that caused that program to exit.:
在 Windows 上,我使用的技术是创建一个全局命名对象(例如使用 CreateMutex 的互斥体),然后让监视程序打开相同的命名互斥体并等待它(使用 WaitForSingleObject)。 一旦第一个程序退出,第二个程序就获得互斥体并知道第一个程序已退出。
在 Unix 上,解决此问题的常用方法是让第一个程序将其 pid (getpid()) 写入文件。 第二个程序可以监视这个 pid(使用kill(pid, 0))来查看第一个程序是否已经消失。 这种方法会受到竞争条件的影响,毫无疑问有更好的方法来解决它。
On Windows, a technique I've used is to create a global named object (such as a mutex with CreateMutex), and then have the monitoring program open that same named mutex and wait for it (with WaitForSingleObject). As soon as the first program exits, the second program obtains the mutex and knows that the first program exited.
On Unix, a usual way to solve this is to have the first program write its pid (getpid()) to a file. A second program can monitor this pid (using kill(pid, 0)) to see whether the first program is gone yet. This method is subject to race conditions and there are undoubtedly better ways to solve it.
如果您想生成另一个进程,然后在它运行时不执行任何操作,那么大多数高级语言已经具有用于执行此操作的内置程序。 例如,在 Perl 中,有用于运行进程并等待其完成的
system
和反引号,以及诸如 IPC::System::Simple 用于更容易地了解程序如何终止,以及您对发生的事情感到高兴还是悲伤。 使用为您处理所有事情的语言功能比尝试自己做要容易方式。如果您使用的是 Unix 风格的系统,那么您分叉的进程的终止将生成一个 SIGCHLD 信号。 这意味着您的程序可以执行子进程正在运行的其他操作。
捕获 SIGCHLD 信号的方式因您的语言而异。 在 Perl 中,您可以像这样设置信号处理程序:
几乎可以肯定 CPAN 上的模块比上面的示例代码做得更好。 如果您想让程序休眠直到其他进程完成,您可以将
waitpid
与特定进程 ID(而不是全部为 -1)一起使用,并且不使用WNOHANG
。请注意,当您在信号处理程序中时,可能会发生各种奇怪的事情。 可能会出现另一个信号(因此我们使用 while 循环来捕获所有死进程),并且根据您的语言,您可能正在执行另一个操作!
如果您在 Windows 上使用 Perl,则可以使用 Win32::Process 模块生成一个进程,并对生成的对象调用
->Wait
以等待其终止。 我不熟悉Win32::Process
的所有内容,但您应该能够等待0
(或1
> 一毫秒)检查进程是否已死亡。在其他语言和环境中,您的里程可能会有所不同。 请确保当您的其他进程终止时,您会检查它如何终止。 由于用户杀死子进程而导致子进程死亡通常需要与子进程因成功完成其任务而退出不同的响应。
祝一切顺利,
保罗
If you want to spawn another process, and then do nothing while it runs, then most higher-level languages already have built-ins for doing this. In Perl, for example, there's both
system
and backticks for running processes and waiting for them to finish, and modules such as IPC::System::Simple for making it easier to figure how the program terminated, and whether you're happy or sad about that having happened. Using a language feature that handles everything for you is way easier than trying to do it yourself.If you're on a Unix-flavoured system, then the termination of a process that you've forked will generate a SIGCHLD signal. This means your program can do other things your child process is running.
Catching the SIGCHLD signal varies depending upon your language. In Perl, you set a signal handler like so:
There's almost certainly modules on the CPAN that do a better job than the sample code above. You can use
waitpid
with a specific process ID (rather than -1 for all), and withoutWNOHANG
if you want to have your program sleep until the other process has completed.Be aware that while you're inside a signal handler, all sorts of weird things can happen. Another signal may come in (hence we use a while loop, to catch all dead processes), and depending upon your language, you may be part-way through another operation!
If you're using Perl on Windows, then you can use the Win32::Process module to spawn a process, and call
->Wait
on the resulting object to wait for it to die. I'm not familiar with all the guts ofWin32::Process
, but you should be able to wait for a length of0
(or1
for a single millisecond) to check to see if a process is dead yet.In other languages and environments, your mileage may vary. Please make sure that when your other process dies you check to see how it dies. Having a sub-process die because a user killed it usually requires a different response than it exiting because it successfully finished its task.
All the best,
Paul