如何避免 <defunct> 进程?
How to avoid <defunct> processes?
我从主控主机到从属主机执行 ZFS 远程复制,我有一个在主控主机上运行的 Perl 脚本。
对于每个文件系统,它通过 ssh 连接到远程主机并以侦听模式启动 mbuffer,然后脚本继续发送数据。成功时 mbuffer 应该自行退出。
问题
很难通过 ssh 在远程主机上启动 mbuffer 然后能够在脚本中继续。我最终做了你在下面看到的事情。
问题是,在脚本退出之前,它会留下 <defunct>
为每个文件系统处理一个。
问题
是否可以避免 <defunct>
进程?
sub mbuffer {
my ($id, $zfsPath) = @_;
my $m = join(' ', $mbuffer, '-I', $::c{port});
my $z = join(' ', $zfs, 'receive', , $zfsPath);
my $c = shellQuote($ssh, $::c{slaves}{$id}, join('|', $m, $z));
my $pm = Parallel::ForkManager->new(1);
my $pid = $pm->start;
if (!$pid) {
no warnings; # fixes "exec" not working
exec($c);
$pm->finish;
}
sleep 3; # wait for mbuffer to listen
return $pid;
}
当您创建一个进程时,它会一直存在直到 parent 获取它。 (如果它的 parent 先退出,它会得到 auto-reaped。)一个进程可以使用 wait
or waitpid
获得它的 children。它还可以导致其 children 在创建 child.
之前使用 local $SIG{CHLD} = 'IGNORE';
自动收割
请注意,Parallel::ForkManager 不是启动单个 child 的正确工具。产生单个工人不是它的目的。
use String::ShellQuote qw( shell_quote );
sub mbuffer {
my ($id, $zfsPath) = @_;
my $mbuffer_cmd = shell_quote($mbuffer, '-I', $::c{port});
my $zfs_cmd = shell_quote($zfs, 'receive', $zfsPath);
my $remote_cmd = "$mbuffer_cmd | $zfs_cmd";
my $local_cmd = shell_quote($ssh, $::c{slaves}{$id}, $remote_cmd);
# open3 will close this handle.
# open3 doesn't deal well with lexical handles.
open(local *CHILD_STDIN, '<', '/dev/null') or die $!;
return open3('<&CHILD_STDIN', '>&STDOUT', '>&STDERR', $local_cmd);
}
IPC::Open3 级别很低,但它最接近您现有的代码。启动进程的更好方法包括 IPC::Run3 和 IPC::Run.
一方面,没有理由对一个进程使用P::FM
。此外,它在这里是不利的,因为您放弃了对流程管理的更好控制。
但是这里的直接错误是在exec
的使用上;这个 post 只解决了这个问题。
exec 调用用另一个程序替换进程中的内容,从不 returns。因此 exec
之后的子代码中的代码不会 运行 (请参阅文档)。因此 $pm->finish
被挂起,子进程永远不会被回收,并且 OS 将其信息保存在进程 table 中,所以有一个 defunct/zombie.
这是直接使用 exec
启动另一个程序的基本方法
my $cmd = '...';
my $pid = fork // die "Can't fork: $!";
if ($pid == 0) {
exec $cmd;
die "exec shouldn't return: $!";
}
my $gone = waitpid $pid, 0;
if ($gone > 0) { say "Child $gone exited with $?" }
elsif ($gone < 0) { say "No $pid process ($gone), reaped already?" }
else { say "Process $pid still running?" }
此处子级从父级继承标准流。此外,在某些情况下,错误报告是粗略的(不精确的),请参阅 ikegami 的评论。
ikegami 的 .
中更详细、更忠实地替代了您所做的工作
我从主控主机到从属主机执行 ZFS 远程复制,我有一个在主控主机上运行的 Perl 脚本。
对于每个文件系统,它通过 ssh 连接到远程主机并以侦听模式启动 mbuffer,然后脚本继续发送数据。成功时 mbuffer 应该自行退出。
问题
很难通过 ssh 在远程主机上启动 mbuffer 然后能够在脚本中继续。我最终做了你在下面看到的事情。
问题是,在脚本退出之前,它会留下 <defunct>
为每个文件系统处理一个。
问题
是否可以避免 <defunct>
进程?
sub mbuffer {
my ($id, $zfsPath) = @_;
my $m = join(' ', $mbuffer, '-I', $::c{port});
my $z = join(' ', $zfs, 'receive', , $zfsPath);
my $c = shellQuote($ssh, $::c{slaves}{$id}, join('|', $m, $z));
my $pm = Parallel::ForkManager->new(1);
my $pid = $pm->start;
if (!$pid) {
no warnings; # fixes "exec" not working
exec($c);
$pm->finish;
}
sleep 3; # wait for mbuffer to listen
return $pid;
}
当您创建一个进程时,它会一直存在直到 parent 获取它。 (如果它的 parent 先退出,它会得到 auto-reaped。)一个进程可以使用 wait
or waitpid
获得它的 children。它还可以导致其 children 在创建 child.
local $SIG{CHLD} = 'IGNORE';
自动收割
请注意,Parallel::ForkManager 不是启动单个 child 的正确工具。产生单个工人不是它的目的。
use String::ShellQuote qw( shell_quote );
sub mbuffer {
my ($id, $zfsPath) = @_;
my $mbuffer_cmd = shell_quote($mbuffer, '-I', $::c{port});
my $zfs_cmd = shell_quote($zfs, 'receive', $zfsPath);
my $remote_cmd = "$mbuffer_cmd | $zfs_cmd";
my $local_cmd = shell_quote($ssh, $::c{slaves}{$id}, $remote_cmd);
# open3 will close this handle.
# open3 doesn't deal well with lexical handles.
open(local *CHILD_STDIN, '<', '/dev/null') or die $!;
return open3('<&CHILD_STDIN', '>&STDOUT', '>&STDERR', $local_cmd);
}
IPC::Open3 级别很低,但它最接近您现有的代码。启动进程的更好方法包括 IPC::Run3 和 IPC::Run.
一方面,没有理由对一个进程使用P::FM
。此外,它在这里是不利的,因为您放弃了对流程管理的更好控制。
但是这里的直接错误是在exec
的使用上;这个 post 只解决了这个问题。
exec 调用用另一个程序替换进程中的内容,从不 returns。因此 exec
之后的子代码中的代码不会 运行 (请参阅文档)。因此 $pm->finish
被挂起,子进程永远不会被回收,并且 OS 将其信息保存在进程 table 中,所以有一个 defunct/zombie.
这是直接使用 exec
my $cmd = '...';
my $pid = fork // die "Can't fork: $!";
if ($pid == 0) {
exec $cmd;
die "exec shouldn't return: $!";
}
my $gone = waitpid $pid, 0;
if ($gone > 0) { say "Child $gone exited with $?" }
elsif ($gone < 0) { say "No $pid process ($gone), reaped already?" }
else { say "Process $pid still running?" }
此处子级从父级继承标准流。此外,在某些情况下,错误报告是粗略的(不精确的),请参阅 ikegami 的评论。
ikegami 的