我如何编写一个程序来告知其他程序何时结束?

时间:2020-03-06 14:38:43  来源:igfitidea点击:

我如何编写一个程序来告知其他程序何时结束?

解决方案

这称为"停止问题",无法解决。
参见http://en.wikipedia.org/wiki/Halting_problem

如果我们要分析一个程序而不执行,那将是一个无法解决的问题。

在Windows上,我使用的一种技术是创建一个全局命名对象(例如带有CreateMutex的互斥锁),然后让监视程序打开该相同的命名互斥锁并等待它(使用WaitForSingleObject)。一旦第一个程序退出,第二个程序就会获得互斥锁并知道第一个程序已退出。

在Unix上,解决此问题的常用方法是让第一个程序将其pid(getpid())写入文件。第二个程序可以监视此pid(使用kill(pid,0))以查看第一个程序是否消失了。此方法受比赛条件的影响,毫无疑问,有更好的方法来解决它。

大多数操作系统通常具有相同的功能。

我们记录有问题的程序的进程ID,并通过定期查询活动进程来对其进行监视

至少在Windows中,我们可以触发事件来执行此操作...

在我们自己没有产生的程序上执行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;
    }
}

嗯,你不能,鉴于它的性质,这是不可能完成的任务。

假设我们有一个程序foo,它接受另一个程序foo-sub作为输入。

Foo {
 func Stops(foo_sub) { run foo_sub; return 1; }
}

所有这一切的问题在于,它的设计过于简单了,如果foo-sub是一个永无止境的程序,那么foo本身就永无止境。无法从外部得知foo-sub或者foo是什么导致程序停止,以及由什么决定程序是否仅仅需要一个世纪才能运行?

本质上,这是计算机无法回答的问题之一。有关更完整的概述,Wikipedia对此进行了介绍。

如果要生成另一个进程,然后在其运行时不执行任何操作,则大多数高级语言已经内置了执行此操作的功能。例如,在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上的模块比上面的示例代码做得更好。我们可以使用具有特定进程ID的" waitpid"(而不是全部为-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的不同进程上重新打开)。

另外,我还没有时间测试这段源代码,但是我将它从我的一个有效的应用程序中删除了。