1

这个问题与上一个问题有关: perl run background job and force job to finish?

我以为我有一个脚本可以通过执行成功杀死我的后台作业kill 15, $logger_pid,但结果证明该命令创建了两个作业。请参阅下面的详细信息:

#!/usr/bin/perl
use strict;
# Do a background $USER top every minute
my $cmd = "top -b -d 1 -n 300 -u $ENV{USER} >> logfile";
$SIG{CHLD}="IGNORE"; my $logger_pid;
unless ($logger_pid=fork) { exec($cmd); }

# This actually creates two jobs with consecutive PIDs
# $USER  5609  0.0  0.0  63820  1072 pts/9    S    09:42   0:00 sh -c top -b -d 1 -n 300 -u $USER >> logfile
# $USER  5610  0.6  0.0  10860  1216 pts/9    S    09:42   0:00 top -b -d 1 -n 300 -u $USER

# Do something for a while
foreach my $count (1..5) { print "$count\n"; sleep 1; }
# I thought this first kill command was enough
kill 15, $logger_pid;
# This line is needed to kill the child job
kill 15, ($logger_pid+1);

1;

有人可以告诉我为什么我需要一秒钟kill 15, ($logger-pid+1)才能真正杀死后台工作吗?有没有办法用一个单一的kill语句来做到这一点?

4

1 回答 1

2

sh -c ...用于执行带有 shell 元字符的外部命令的系统(在您的情况下,>>用于输出重定向,以及命令字符串中的任何空格)记录在exec和中system。为避免使用sh -c和创建单个进程,您可以使用以下LIST形式exec

my @cmd = ('top','-b','-d','1','-n','300','-u',$ENV{USER});
exec(@cmd);

输出重定向使这有点棘手,但您可以通过STDOUT在 之后fork和之前关闭并重新打开来完成它exec

my @cmd = ...
close STDOUT;
open STDOUT, '>>', 'logfile';
exec @cmd;

做输出重定向时,重要的是正确设置文件描述符1,不一定STDOUT。例如,这将无法像您希望的那样进行输出重定向。

my @cmd = ...;
close STDOUT;       # close file descriptor 1
open FOO, '>/tmp/asdf';        # probably uses file descriptor 1
open STDOUT, '>>', 'logfile';  # probably uses file descriptor != 1
exec(@cmd);                    # probably writes output to FOO\/tmp/asdf

使用进程组的替代解决方案: @Chris 在评论中建议您向函数发送负值kill以杀死整个进程组。通常,您创建的进程与fork其父进程具有相同的进程组,但是如果您将子进程设置为使用新的进程组,这将起作用:

unless ($logger_pid = fork) {
    use POSIX;
    POSIX::setpgid($$,$$);   # set child process to be in its own process group
    exec($cmd);
}

...

kill -15, $logger_pid;       # kill the process group with id $logger_pid
于 2013-10-22T15:58:21.867 回答