Jas*_*nes 3 linux ssh perl fork zombie-process
我从主控主机到从属主机进行 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;
}
Run Code Online (Sandbox Code Playgroud)
当您创建一个进程时,它会一直存在,直到其父进程收割它为止。(如果其父进程先退出,它将被自动回收。)进程可以使用wait或 来回收其子进程waitpid。local $SIG{CHLD} = 'IGNORE';它还可以通过在创建子级之前使用来自动收割其子级。
请注意,Parallel::ForkManager 不是启动单个子项的正确工具。它的目的不是产生一个工人。
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);
}
Run Code Online (Sandbox Code Playgroud)
IPC::Open3 的级别相当低,但它最接近您现有的代码。启动进程的更好方法包括 IPC::Run3 和 IPC::Run。