Perl CGI退出而不会死亡

时间:2014-02-14 07:20:31

标签: perl fastcgi

如果使用Perl FCGI,如何在不使用exit的情况下结束脚本。搜索了几天后,我发现的唯一解决方案是跳转到主脚本中的标签。下面是主索引的代码.fcgi。

    $fcgi_requests = 0; # the number of requests this fcgi process handled.
$handling_request = 0;
$exit_requested = 0;
$app_quit_request = 0; # End the application but not the FCGI process

# workaround for known bug in libfcgi
while (($ignore) = each %ENV) { }

$fcgi_request = FCGI::Request();
#$fcgi_request = FCGI::Request( \*STDIN, \*STDOUT, \*STDERR, \%ENV, $socket);

sub sig_handler {
    my ($callpackage, $callfile, $callline) = caller;
    if ($app_quit_request) {
        $app_quit_request = 0;
        goto ABORTLABEL;
    }

    $exit_requested = 1;
    exit(0) if !$handling_request;
}

$SIG{USR1} = \&sig_handler;
$SIG{TERM} = \&sig_handler;
$SIG{PIPE} = 'IGNORE';

#The goal of fast cgi is to load the program once, and iterate in a loop for every request.
while ($handling_request = ($fcgi_request->Accept() >= 0)) {
    process_fcgi_request();
    $handling_request = 0;
    last if $exit_requested;
    #exit if -M $ENV{SCRIPT_FILENAME} < 0; # Autorestart
}

$fcgi_request->Finish();
exit(0);
#=========================================================#
sub process_fcgi_request() {
    $fcgi_requests++;

    # dispatch current request
    my_app();

    $fcgi_request->Finish();
}
#=========================================================#
# let it think we are done, used by abort
ABORTLABEL:
    $fcgi_request->Finish();
#=========================================================#

主要请求是我想从子insidi模块内部停止程序执行,这些模块可能被深度调用,例如在帐户模块中的登录功能中。 当然我不能使用exit因为它将终止fcgi进程,我尝试了所有错误并抛出并尝试模块所有使用die也结束了进程。当然我可以使用每个sub的返回但这需要重写fcgi的整个程序。

2 个答案:

答案 0 :(得分:1)

在Perl中建模异常的常规方法是在die内调用eval BLOCK,这会捕获die,因此不会终止进程。它只会终止eval,程序会立即继续运行。据我所知,CPAN上的异常处理模块主要是围绕这个基本功能的包装器,为它提供不同的语法或者更容易编写catch块。所以我很惊讶这些不适合你。你真的试过它们还是只是假设die总是杀了这个过程?这个名字有点误导,因为它的确意味着“抛出异常”。如果你在eval以外的地方做到这一点,那么解释器会抓住它,而它唯一的反应是终止这个过程。

eval {
  say "Hello world";
  die;
  say "Not printed";
};
say "Is printed";

您不想在exit内拨打eval。什么都没有。

我建议尽管重写FCGI的整个控制流程。代码的生命周期发生了显着变化,因此您必须进行一定数量的修改,以确保变量重用正常并且您不会泄漏内存。通常最好事先做好,而不是花几天时间追踪奇怪的错误。

答案 1 :(得分:0)

经过几个问题和深入研究,我得到了这个解决方案。此编码示例允许您从任何嵌套级别的调用返回。模块Scope::Upper是XS,所以它应该很快。

use Scope::Upper qw/unwind CALLER/;

sub level1 {
    print "before level 1 \n";
    level2();
    print "after level 1 \n";
}

sub level2 {
    print "before level 2 \n";
    level3();
    print "after level 2 \n";
}

sub level3 {
    print "before level 3 \n";
    level4();
    print "after level 3 \n";
}

sub level4 {
    print "before level 4 \n";

    #unwind CALLER 2;

    my @frame;
    my $i;
    #$i++ while @frame = caller($i);# and $frame[0] ne "main";
    $i++ while @frame = caller($i);
    #print "i=: $i \n";
    #unwind CALLER (@frame ? $i : $i - 1);
    unwind CALLER $i-1;

    print "after level 4 \n";
}

print level1();

如果您运行此代码,则输出将为:

before level 1 
before level 2 
before level 3 
before level 4 

您可以使用以下方式返回任何更高级别:

my intLevel = 2;    
unwind CALLER intLevel;