当 运行 进程不是子进程时,如何在 perl 中等待 运行 进程完成?

How to wait for running process to complete in perl when running process is not child process?

我正在浏览一个正在使用的 Perl 脚本 waitpid($pid, 0) 等待当前进程完成。 但是 print 在这个 waitpid 之后写的语句在过程完成之前打印它。

我想知道为什么 waitpid 不先等待进程完成。

另外,运行 进程的控制在不同的模块下,不是这个 perl 脚本的一部分。只能访问进程的 pid 和名称。我无法更改调用该过程的模块中的任何内容。

waitpid documentation 状态:

Waits for a particular child process to terminate and returns the pid of the deceased process, or -1 if there is no such child process.

快速测试:

my $pid = open my $fh,"-|","sleep 3";
print waitpid(28779,0); # Some other process
print waitpid($pid,0);

28779 是当前 运行 的另一个进程(从 ps axu 中随机抽取一个)。输出:

-1
4088

您不能使用 waitpid 等待不是当前进程子进程的进程。

The kill command 可以检查 PID 当前是否为 运行:

print kill(0,28779);

输出:

1

您仍然需要轮询(睡眠循环)让 pid 消失。还要记住,受监视的进程可能会退出,并且新的进程可能会在您下一次检查之前重用相同的 PID(不太可能,但有可能)。

注意 一个 simple-minded one-liner 和 kill 0, $pid 在最后,评论。


我们需要检测外部程序是否已完成,但该脚本并未启动该程序。该问题询问有关使用 waitpid 的问题。复制我的早期评论:

You cannot. You can only wait on a child process. See perldoc wait (or waitpid, it's the same), first sentence.

waitwaitpid 等待发送到脚本的关于其 child(ren) 命运的信号。脚本没有理由接收有关它未启动的进程的此类信号。


我们知道进程的 ID 和名称。它的PID可以用来轮询是否是运行ning。单独使用 pid 并不完全可靠,因为在我们的检查之间,进程可以完成并且随机分配一个新的相同 pid。我们可以使用程序的名称来加强这一点。

在 Linux 系统上,可以通过使用(许多ps 选项获取有关进程的信息。其中任何一个 returns 程序的完整调用

ps --no-headers -o cmd PID
ps --no-headers -p PID -o cmd

返回的字符串可能以解释程序的路径(例如,对于 Perl 脚本)开头,后跟程序的全名。版本 ps -p PID -o comm= returns 只有程序的名称,但我发现它可能会在连字符(如果有)上打断那个词,导致名称不完整。这可能需要在某些系统上进行调整,请咨询您的 man ps。如果没有给定 PID 的进程,我们什么也得不到。

然后我们可以检查 PID,如果找到,检查该 PID 的名称是否与程序匹配。该程序的名称是已知的,我们可以对其进行硬编码。但是,它仍然由脚本在启动时使用上面的 ps 命令获取,以避免歧义。 (然后它也是相同的格式以供以后比较。)这​​本身会根据已知名称进行检查,因为不能保证脚本执行时的 PID 确实是预期程序。

use warnings;
use strict;

# For testing. Retrieve your PID as appropriate for real use    
my $ext_pid = $ARGV[0] || $$;

my $cmd_get_name = "ps --no-headers -o cmd $ext_pid";

# For testing.  Replace 'sleep' by your program name for real use
my $known_prog_name = 'sleep';

# Get the name of the program with PID
my $prog_name = qx($cmd_get_name);

# Test against the known name, exit if there is a mismatch
if ($prog_name !~ $known_prog_name) {
    warn "Mismatch between:\n$prog_name\n$known_prog_name -- $!";
    exit;
}   

my $name;
while ( $name = qx($cmd_get_name) and $name =~ /$prog_name/ )
{
    print "Sleeping 1 sec ... \n";
    sleep 1;
}   
# regex above may need slight adjustment, depending on format of ps return

通过上面的 qx()(反引号运算符)收到的命令输出包含换行符。如果这被证明是脚本执行的问题,则可以对其进行 chomp 编辑,这需要稍作调整。剩下的漏洞是 那个 程序可能已经完成并在检查之间重新启动,并且具有相同的 PID。

这将由 运行ning 在 shell

中进行测试
sleep 30 &
script.pl `ps aux | egrep '[s]leep'`

egrepgrep -E`ps ...` 的输出包含多个单词。这些作为命令行参数传递给我们的脚本,它使用第一个作为 PID。如果它有问题 运行 首先 ps 过滤然后手动输入 PID 作为脚本的输入参数。上面的30秒sleep是为了给命令行足够的时间做这一切。

可以通过$name匹配一个hard-coded$prog_name来简化代码,如果程序名足够唯一并且不会改变的话。 hard-coded 名称 是上面使用的 ,但用于检查,如果不匹配它会生成警告。 (如果我们仅依赖于硬编码,那么如果它不匹配,我们将无法发出警告,因为那是代码操作的一部分。)


如果进程与脚本属于同一用户,则可以使用 kill 0, $pid,如

while ( kill 0, $ext_pid ) { sleep 1 }

然后您要么必须再次调用以检查名称,要么满足于 $pid 代表的实际过程中出现错误的(小)可能性。

模块 Proc::ProcessTable 可用于其中的大部分内容