【问题标题】:How can I detach a process from a CGI so I can store and read files from memory?如何从 CGI 中分离进程,以便从内存中存储和读取文件?
【发布时间】:2011-01-10 00:49:35
【问题描述】:

我是否有可能从 CGI 脚本中生成一个分离的守护进程之类的进程 将读取的文本文件存储在内存中,然后在下一次 cgi 执行中重新访问内存,使用管道读取数据?

大多数托管 ISP 是否允许分离进程?内存管道是否快速且易于在 unix/linux 系统上编码/使用?

是否有无需使用任何额外 CPAN 模块即可完成的解决方案?这是一个 CGI 过程,所以我想尽量减少它。

【问题讨论】:

    标签: perl caching cgi pipe named-pipes


    【解决方案1】:

    如果您绝对希望文件的内容存在于内存中,一个更简单的解决方案是创建一个 RAM 磁盘并将它们存储在那里。这样你就不必对 cgi 脚本做任何特别的事情了。

    【讨论】:

    • 我正在寻找一种便携式解决方案...这在普通共享网络托管服务器上有两项工作
    • @Jera - 我怀疑 ram-disk 解决方案的可移植性不如 spawning deamons and communicating with pipes
    • 怎么回事?我不是 ram 磁盘方面的专家,如何在主机上轻松设置它?所有主机都允许吗?
    【解决方案2】:

    假设你有一个简单的resource.cgi

    #! /usr/bin/perl
    
    use warnings;
    use strict;
    
    use Reader;
    use CGI qw/ :standard /;
    
    print header("text/plain"),
          "Contents:\n",
          Reader::data,
          "-" x 40, "\n";
    

    它的输出是

    内容类型:文本/纯文本;字符集=ISO-8859-1
    
    内容:
    这是一个数据文件
    有一些非常有趣的
    位。
    ----------------------------------------------------

    有趣的部分在Reader.pm,它以看起来很熟悉的样板开头:

    package Reader;
    
    use warnings;
    use strict;
    
    use Fcntl qw/ :DEFAULT :flock :seek /;
    use POSIX qw/ setsid /;    
    

    接下来它定义了集合点:

    my $PIDFILE = "/tmp/reader.pid";
    my $DATA    = "/tmp/file.dat";
    my $PIPE    = "/tmp/reader.pipe";
    

    import is called as part of use Module。如果守护程序已经在运行,则无需执行任何操作。否则,我们分叉守护进程并将其进程 ID 写入$PIDFILE

    sub import {
      return unless my $fh = take_lock();
    
      my $child = fork;
      die "$0: fork: $!" unless defined $child;
    
      if ($child) {
        print $fh  "$child\n" or die "$0: write $PIDFILE: $!";
        close $fh             or die "$0: close $PIDFILE: $!";
        return;
      }
    
      # daemonize
      close $fh;
      chdir "/";
      open STDIN,  "<", "/dev/null";
      open STDOUT, ">", "/dev/null";
      open STDERR, ">", "/dev/null";
      setsid;
    
      open $fh, "<", $DATA or die;
      undef $/;
      my $data = <$fh>;
      close $fh;
    
      while (1) {
        open my $fh, ">", $PIPE or die;
        print $fh $data         or die;
        close $fh;
      }
    }
    

    每个客户端都需要等待轮到自己锁定$PIDFILE。获得锁后,我们将检查识别的进程是否仍在运行,并在必要时创建命名管道。

    sub take_lock {
      sysopen my $fh, $PIDFILE, O_RDWR | O_CREAT or die "$0: open $PIDFILE: $!";
      flock $fh => LOCK_EX                       or die "$0: flock $PIDFILE: $!";
    
      my $pid = <$fh>;
    
      if (defined $pid) {
        chomp $pid;
    
        if (kill 0 => $pid) {
          close $fh;
          return;
        }
      }
      else {
        die "$0: readline $PIDFILE: $!" if $!;
      }
    
      sysseek  $fh, 0, SEEK_SET or die "$0: sysseek $PIDFILE: $!";
      truncate $fh, 0           or die "$0: truncate $PIDFILE: $!";
    
      unless (-p $PIPE) {
        system("mknod", $PIPE, "p") == 0
                                or die "$0: mknod exited " . ($? >> 8);
      }
    
      $fh;
    }
    

    最后,读取管道很简单:

    sub data {
      open my $fh, "<", $DATA or die "$0: open $DATA: $!";
      local $/;
      scalar <$fh>;
    }
    

    别忘了从模块中返回一个真值:

    1;
    

    您会注意到守护程序中的操作仍然可能失败。为了您的理智,您将希望以某种方式记录事件,而不是默默地窒息。

    至于主机是否允许长时间运行的进程,这将因提供商而异,但即使您的守护程序不时被杀死,上面的代码也会按需重新启动它。

    【讨论】:

      【解决方案3】:

      为什么要这样做?你想解决什么问题?像File::Map 这样的东西有用吗?它映射文件,因此文件不在内存中,但它们的行为就像它们一样。我在Memory-map files instead of slurping them 中写了一些关于此的内容。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-06-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-08-09
        • 1970-01-01
        • 1970-01-01
        • 2016-08-29
        相关资源
        最近更新 更多