perl - Perl 中什么情况下会跳过 END block ?

标签 perl

我有一个长时间运行的程序,它使用 File::Temp::tempdir 创建临时文件,有时会通过 ^C 中断它。

以下程序打印它创建的临时目录的名称以及其中文件的名称。

#!/usr/bin/env perl
use strict;
use warnings;
use File::Temp qw[tempdir];

my $dir = tempdir(CLEANUP => 1);
print "$dir\n";
print "$dir/temp.txt\n";

`touch $dir/temp.txt`;
exit;

在 OS X 上,这会在 /var/folders 内创建一个目录

如果最后一行是 exit;die;,则该文件夹将被清理,其中的临时文件将被删除。

但是,如果我们将最后一行替换为 sleep 20;,然后通过 ^C 中断 perl 程序,临时目录仍然存在。

% perl maketemp.pl
/var/folders/dr/cg4fl5m11vg3jfxny3ldfplc0000gn/T/ycilyLSFs6
/var/folders/dr/cg4fl5m11vg3jfxny3ldfplc0000gn/T/ycilyLSFs6/temp.txt
^C
% stat /var/folders/dr/cg4fl5m11vg3jfxny3ldfplc0000gn/T/ycilyLSFs6/temp.txt
16777220 6589054 -rw-r--r-- 1 <name> staff 0 0 "Aug  1 20:46:27 2016" "Aug  1 20:46:27 2016" "Aug  1 20:46:27 2016" "Aug  1 20:46:27 2016" 4096 0 0 
/var/folders/dr/cg4fl5m11vg3jfxny3ldfplc0000gn/T/ycilyLSFs6/temp.txt
%

使用仅调用 exit; 的信号处理程序确实会清理目录。例如

#!/usr/bin/env perl
use strict;
use warnings;
use File::Temp qw[tempdir];

$SIG{INT} = sub { exit; };

my $dir = tempdir(CLEANUP => 1);
print "$dir\n";
print "$dir/temp.txt\n";

`touch $dir/temp.txt`;
sleep 20;

就像使用“简单的”信号处理程序一样

#!/usr/bin/env perl
use strict;
use warnings;
use File::Temp qw[tempdir];

$SIG{INT} = sub { };

my $dir = tempdir(CLEANUP => 1);
print "$dir\n";
print "$dir/temp.txt\n";

`touch $dir/temp.txt`;
sleep 20;

我尝试查看源代码 ( https://github.com/Perl-Toolchain-Gang/File-Temp/blob/master/lib/File/Temp.pm ) 以确定 tempdir 如何注册清理操作

这是退出处理程序安装

https://github.com/Perl-Toolchain-Gang/File-Temp/blob/master/lib/File/Temp.pm#L1716

调用_deferred_unlink

https://github.com/Perl-Toolchain-Gang/File-Temp/blob/master/lib/File/Temp.pm#L948

它修改了全局哈希%dirs_to_unlink%files_to_unlink,但出于某种原因使用pid $$作为键(可能在如果 Perl 解释器 fork ?不知道为什么这是必要的,因为删除目录似乎是一个幂等操作。)

清理文件的实际逻辑在这里,在 END block 中。

https://github.com/Perl-Toolchain-Gang/File-Temp/blob/master/lib/File/Temp.pm#L878

快速实验表明,当 perl 正常或异常退出时,END block 确实会运行。

sleep 20;

END {
    print "5\n";
}

# does not print 5 when interrupted

并在这里运行

$SIG{INT} = sub {};
sleep 20;

END {
    print "5\n";
}

# does print 5 when interrupted

那么...为什么在 SIGINT 之后会跳过 END block ,除非有一个信号处理程序,即使是一个看起来不应该执行任何操作的信号处理程序?

最佳答案

默认情况下,SIGINT 会终止进程[1]。我所说的“kill”是指该进程立即被内核终止。该进程不会执行任何清理。

通过为 SIGINT 设置处理程序,您可以覆盖此行为。不是终止进程,而是调用信号处理程序。它可能不会做任何事情,但它的存在本身就阻止了进程被杀死。在这种情况下,程序不会因信号而退出,除非它选择退出(通过在处理程序中调用 dieexit 。如果确实如此,则它会退出)将有机会像平常一样进行清理。

请注意,如果在系统调用期间传入已定义处理程序的信号,则系统调用会以错误 EINTR 退出,以便程序安全地处理该信号。这就是为什么 sleep 在收到 SIGINT 后立即返回的原因。

如果您使用 $SIG{INT} = 'IGNORE';,则该信号将被完全忽略。任何正在进行的系统调用都不会被中断。

<小时/>
  1. 在我的系统上,man 1 Kill 列出了信号的默认操作。

关于perl - Perl 中什么情况下会跳过 END block ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38711725/

相关文章:

perl - 如何检查 64 位整数的可用性(对于 `use integer` )?

perl - 为什么IPC::Open3陷入僵局?

perl - 如何在 Perl 中用子类覆盖父类函数?

perl - 无法访问 perl 脚本中的 shell 变量,除非它是环境变量

perl - 如何在Perl中 "nicely"检测类的直接父类?

perl - 无法在 perl 中获取 CSV 格式的所需哈希

string - perl 使用正则表达式拆分字符串,但将分隔符连接到输出

perl - 用 Perl 编写支持 Unicode 的单行程序

bash - IO::Uncompress::Gunzip 在 "original"gzipped 文件内的第一个 "concatenated"gzipped 文件后停止

perl - 在不占用所有 CPU 的情况下运行守护程序循环 (Perl)