【问题标题】:How do I fork properly with mod_perl2?如何使用 mod_perl2 正确分叉?
【发布时间】:2010-10-03 01:39:41
【问题描述】:

我在从一些在 mod_perl2 下运行的代码分叉一个长时间运行的进程时遇到了麻烦。

大部分情况下一切正常,但似乎分叉的进程正在持有 Apache 日志文件的打开句柄 - 这意味着 Apache 在进程运行时不会重新启动(我收到“无法打开日志文件”消息)。

这是我正在使用的代码:

use POSIX; # required for setsid

# Do not wait for child processes to complete
$SIG{CHLD} = 'IGNORE';

# fork (and make sure we did!)
defined (my $kid = fork) or die "Cannot fork: $!\n";

if ($kid) {
    return (1, $kid);
}else {
    # chdir to /, stops the process from preventing an unmount
    chdir '/' or die "Can't chdir to /: $!";

    # dump our STDIN and STDOUT handles
    open STDIN, '/dev/null' or die "Can't read /dev/null: $!";
    open STDOUT, '>/dev/null' or die "Can't write to /dev/null: $!";

    # redirect for logging
    open STDERR, '>', $log_filename or die "Can't write to log: $!";

    # Prevent locking to apache process
    setsid or die "Can't start a new session: $!";

    # execute the command
    exec( $cmd, @args );

    die "Failed to exec";
}

在mod_perl1的日子里,我记得使用$r->cleanup_for_exec来解决这个问题,但是在mod_perl2下似乎不支持它。 (编辑:Apparently 不再需要了..)

任何关于如何从 mod_perl2 正确启动长时间运行的进程而没有这些问题的建议将不胜感激!

【问题讨论】:

  • 嗨丹,你是怎么解决这个问题的,我也面临同样的问题。早些时候我使用 mod_perl1 和 $r->cleanup_for_exec 工作正常,但在 mod_perl2 中不再需要,所以请你帮我在 mod_perl2 中实现它吗?提前致谢。

标签: perl mod-perl2


【解决方案1】:

您可能想阅读this 讨论。除非您知道如何准备东西,否则似乎您不应该使用 mod_perl。您必须使用诸如Apache2::SubProcess之类的模块

【讨论】:

    【解决方案2】:

    尝试在分叉之前关闭您的 STDIN/STDOUT 句柄。

    【讨论】:

    • 那将彻底破坏 Apache.. 父进程仍然必须为客户端生成响应(并通过 STDOUT 发送)..
    • 父进程可以在分叉前发送响应;除非您实际上在分叉后在父级中做更多的工作。如果日志文件不在 STDIN/OUT/ERR 描述符上,您可以开始关闭在子进程中找到的任何打开的描述符 >2。
    • 是的,问题是 apache 进程在完成这个请求后继续服务其他请求,杀死它的 STDERR 会破坏它的日志记录,我怀疑杀死它的 STDIN 会阻止它与父进程通信。无论如何-我试过了,但没有解决问题;)
    • 尝试在孩子中从 3 循环到 1024,执行 'open($fh, "
    • 是的,你必须 fork 两次,一次在 setsid 之前(创建新进程),另一次在 setsid 之后(确保你的会话和进程组没有领先的进程)。
    【解决方案3】:

    在我的(以前的 mod_perl,现在的 FCGI)代码中,我在“if ($kpid)”的“else”子句中,

        close STDIN;
        close STDOUT;
        close STDERR;
        setsid();
    

    另外,由于我忘记的原因,我立即再次 fork,然后在 那个子中重新打开 STDIN、STDOUT 和 STDERR。

    所以它看起来像:

    $SIG{CHLD} = 'IGNORE';
    
    # This should flush stdout.
    my $ofh = select(STDOUT);$| = 1;select $ofh;
    
    my $kpid = fork;
    if ($kpid)
    {
        # Parent process
        waitpid($kpid, 0);
    }
    else
    {
        close STDIN;
        close STDOUT;
        close STDERR;
        setsid();
        my $gpid = fork;
        if (!$gpid)
        {
            open(STDIN, "</dev/null") ;#or print DEBUG2 "can't redirect stdin\n";
            open(STDOUT, ">/dev/null") ;#or print DEBUG2 "can't redirect stdout\n";
            open(STDERR, ">/dev/null") ;#or print DEBUG2 "can't redirect stderr\n";
            # Child process
            exec($pgm, @execargs) ;# or print DEBUG2 "exec failed\n";
        }
        exit 0;
    }
    

    【讨论】:

      猜你喜欢
      • 2021-11-07
      • 2012-12-26
      • 1970-01-01
      • 2010-10-25
      • 2019-05-28
      • 1970-01-01
      • 2015-03-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多