使用 open 创建子进程时杀死子进程及其子进程
这是我的代码,为了清楚起见,删除了错误处理和其他内容:
sub launch_and_monitor {
my ($script, $timeout) = @_;
sub REAPER {
while ((my $child = waitpid(-1, &WNOHANG)) > 0) {}
$SIG{CHLD} = \&REAPER;
}
$SIG{CHLD} = \&REAPER;
my $pid = fork;
if (defined $pid) {
if ($pid == 0) {
# in child
monitor($timeout);
}
else {
launch($script);
}
}
}
启动子程序执行一个 shell 脚本,该脚本又启动其他进程,如下所示:
sub launch($) {
my ($script) = @_;
my $pid = open(PIPE, "$script|");
# write pid to pidfile
if ($pid != 0) {
while(<PIPE>) {
# do stuff with output
}
close(PIPE) or die $!;
}
}
监视器子程序基本上只是等待指定的时间段,然后尝试终止 shell脚本。
sub monitor($) {
my ($timeout) = @_;
sleep $timeout;
# check if script is still running and if so get pid from pidfile
if (...) {
my $pid = getpid(...);
kill 9, $pid;
}
}
这会杀死脚本,但是,它不会杀死其任何子进程。如何修复它?
Here's my code, with error handling and other stuff removed for clarity:
sub launch_and_monitor {
my ($script, $timeout) = @_;
sub REAPER {
while ((my $child = waitpid(-1, &WNOHANG)) > 0) {}
$SIG{CHLD} = \&REAPER;
}
$SIG{CHLD} = \&REAPER;
my $pid = fork;
if (defined $pid) {
if ($pid == 0) {
# in child
monitor($timeout);
}
else {
launch($script);
}
}
}
The launch sub executes a shell script which in turn launches other processes, like so:
sub launch($) {
my ($script) = @_;
my $pid = open(PIPE, "$script|");
# write pid to pidfile
if ($pid != 0) {
while(<PIPE>) {
# do stuff with output
}
close(PIPE) or die $!;
}
}
The monitor sub basically just waits for a specified period of time and then attempts to kill the shell script.
sub monitor($) {
my ($timeout) = @_;
sleep $timeout;
# check if script is still running and if so get pid from pidfile
if (...) {
my $pid = getpid(...);
kill 9, $pid;
}
}
This kills the script, however, it does not kill any of its subprocesses. How to fix it?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
如果您的操作系统支持,您可以使用进程组来执行此操作。你需要让脚本进程成为进程组的领导者。它运行的子进程将从其父进程继承进程组。然后,您可以使用kill同时向组中的每个进程发送信号。
在
launch()
中,您需要将open
行替换为 fork 行。然后在子进程中,您将在执行命令之前调用 setpgrp() 。类似下面的内容应该可以工作:稍后,要终止脚本进程及其子进程,请否定您发出信号的进程 ID:
You can do this with process groups, if your operating system supports them. You need to make the script process become a process group leader. The child processes that it runs will inherit the process group from their parent. You can then use kill to send a signal to each process in the group at the same time.
In
launch()
, you will need to replace theopen
line with one that forks. Then in the child, you would callsetpgrp()
before exec'ing the command. Something like the following should work:Later, to kill the script process and its children, negate the process ID that you're signalling:
一般来说,我认为您不能期望信号传播到所有子进程中;这不是 Perl 特有的。
也就是说,您也许可以使用 Perl 内置的进程组信号功能
您可能需要使用 setpgrp() 在你的(直接)子进程上,然后将你的kill调用更改为:
In general, I don't think you can expect signals to be propagated into all child processes; this isn't specific to perl.
That said, you might be able to use the process group signal feature built into perl kill():
You probably need to use setpgrp() on your (direct) child process, then change your kill call to something like:
尝试
在
launch_and_monitor
函数顶部 添加:这会将您的进程置于单独的会话中,并导致会话领导者(即主进程)退出时退出。Try adding:
at the top of your
launch_and_monitor
function. This will put your processes in a separate session, and cause things to exit when the session leader (i.e. the master) exits.杀死一个进程组是可行的,但不要忘记父进程也可以被单独杀死。假设子进程有一个事件循环,它们可以在执行 fork() 之前检查在套接字对中创建的父套接字的有效性。事实上,当父套接字消失时,select()会干净地退出,所需要做的就是检查套接字。
例如:
运行如下:
Killing a processgroup works, but don't forget the parent can be killed alone too. Assuming child processes have an event loop, they can check the parent socket that was created in a socketpair prior doing the fork() for validness. In fact, select() cleanly exits when the parent socket is gone, all that needs to be done is to check the socket.
E.g.:
Runs like this: