1

サーバーを作成するために、Lincoln Stein の優れたNetwork Programming With Perlの本からいくつかのイディオムを使用しています。フォークの前に宣言され、後で参照される変数に対して奇妙な動作が発生しているようです。

問題を説明する完全なプログラムを次に示します。(これ以上簡素化されていないことをお詫びします。無関係だと思ったものをすべて取り除いたところ、問題は解決しました。) を探す##### MYSTERY #####と、宣言の 2 つのバージョンが表示されますmy $pid。1 つのバージョンは機能しますが、もう 1 つのバージョンは機能しません。子 PID を $pid に割り当てるへの呼び出しに続いて、become_daemon()それを PID ファイルに書き込んで、これが機能することを確認します。使用した宣言方法に応じて、成功するか失敗します。理解できません!

#!/usr/bin/perl 
#
# Prototype contactd master server

use warnings;
use strict;
use Carp;
use Getopt::Std;
use File::Basename;
use IO::Socket;
use IO::File;
use Net::hostent;    # for OO version of gethostbyaddr
use POSIX qw{WNOHANG setsid};
use Data::Dumper;

#use 5.010;
sub say { print "@_\n"; }

my $program        = basename $0;
my $default_config = "$program.config";

$| = 1;              # flush STDOUT buffer regularly

my %opts;

my $config_file = $opts{c} || $default_config;

# Process the config file to obtain default settings
#
# Note: for now we'll hard code config values into the config hash.
#
my %config;

$config{PORT}      = 2000;
$config{DAEMONIZE} = 0;
$config{VERBOSE}   = 0;
$config{LOGDIR}    = "/mxhome/charrison/private/wdi/logs";
$config{PIDFILE}   = "/var/tmp/$program.pid";

# Process command line args to override default settings
#
my $server_port = $opts{p} || $config{PORT};
my $log_dir     = $opts{l} || $config{LOGDIR};
my $verbose   = !!( exists $opts{v} || $config{VERBOSE} );
my $daemonize = !!( exists $opts{d} || $config{DAEMONIZE} );
my $pid_file = $opts{P} || $config{PIDFILE};

################################################################################
# Set up signal handlers
#
# Caution: these call the logging manager servlog(), which has not yet been
# spawned.
################################################################################

# Set up a child-reaping subroutine for SIGCHLD
#
$SIG{CHLD} = sub {
    local ( $!, $^E, $@ );
    while ( ( my $kid = waitpid( -1, WNOHANG ) ) > 0 ) {
    }
};

# Set up a signal handler for interrupts
#
my $quit = 0;
$SIG{INT} = sub {
    $quit++;
};

# Set up signal handler for pipe errors
#
$SIG{PIPE} = sub {
    local ( $!, $^E, $@ );

};

################################################################################
#                           DAEMONIZATION OCCURS HERE
################################################################################

my $pid_fh = open_pid_file($pid_file);

##### MYSTERY #####

my $pid;           # this makes it work
# my $pid = $$;    # this breaks it!

$daemonize = 1;  # inserted here for demo

if ($daemonize) {
    say "Becoming a daemon and detaching from your terminal.  Bye!";
    $pid = become_daemon();    # update w/new pid
}

say "Here is pid: $pid.  Going to write it to $pid_file and close.";

# If we daemonized, then we are now executing with a different PID
#
# And in that case, the following fails silently!!
#

print $pid_fh $pid;    # store our PID in known location in filesystem
close $pid_fh;

say "Boo boo" if !-e $pid_file;
say qx{cat $pid_file};

##### END OF DEMO #####    

# open_pid_file()
#
# Courtesy of Network Programming with Perl, by Lincoln D. Stein
#
sub open_pid_file {
    my $file = shift;
    if ( -e $file ) {    # PID file already exists
        my $fh = IO::File->new($file) || return;
        my $pid = <$fh>;    # so read it and probe for the process
        croak "Server already running with PID $pid\n"    # die ...
            if kill 0 => $pid;                            # if it responds
        warn "Removing PID file for defunct server process $pid.";
        croak "Can't unlink PID file $file"               # die ...
            unless -w $file && unlink $file;              # if can't unlink
    }
    return IO::File->new( $file, O_WRONLY | O_CREAT | O_EXCL, 0644 )
        or die "Can't create PID file $file: $!\n";
}

# become_daemon()
#
# Courtesy of Network Programming with Perl, by Lincoln D. Stein
#
sub become_daemon {
    die "Can't fork" unless defined( my $child = fork );
    exit 0 if $child != 0;    # die here if parent

    # --- PARENT PROCESS DIES
    # --- CHILD PROCESS STARTS

    setsid();    # Become session leader
    open( STDIN, "</dev/null" );

    # servlog() writes to STDOUT which is being piped to log manager
    #
    #open( STDOUT, ">/dev/null" );
    open( STDERR, ">&STDOUT" );

    chdir '/';    # go to root directory
    umask(0);     # ??
    $ENV{PATH} = '/bin:/sbin:/use/bin:/usr/sbin';
    return $$;
}


END {
    unlink $pid_file if $pid == $$;    # only the daemon unlinks pid file
}
4

1 に答える 1

1

コードの最後には、次のものがあります。

END {
    unlink $pid_file if $pid == $$;    # only the daemon unlinks pid file
}

$pid親プロセスで定義されていない場合、これは正常に機能します。しかし、親プロセスの ID に初期化すると、親は をbecome_daemon呼び出すとすぐに PID ファイルのリンクを解除しますexit

(ここでは、PID ファイルを作成する子とそのリンクを解除する親との間に競合があるように思われるため、結果が常に同じであるとは限りません。 )

編集:実際には、フォークの前に PID ファイルが開かれるため、競合はありません。したがって、親プロセスはファイルを開き、子をフォークし、ファイルのリンクを解除して終了します。子はまだファイルのハンドルを持っており、ファイルに書き込むことができますが、ファイルはファイルシステムのどこからもリンクされておらず、子プロセスが終了するとすぐに消えます。

于 2012-12-29T23:45:36.193 に答える