avo has asked for the wisdom of the Perl Monks concerning the following question:

Dear Fellow Monks, I have a client server application that works the following way: The server has the following portion to handle multiple TCP/IP connections (ie Fork).
sub REAP { 1 until (-1 == waitpid(-1, WNOHANG)); $SIG{CHLD} = \&REAP; } $SIG{CHLD} = \&REAP; my $sock = new IO::Socket::INET( # LocalHost => 'localhost', LocalPort => 123, Proto => 'tcp', Listen => SOMAXCONN, Reuse => 1); $sock or die "no socket :$!"; STARTING: while ($new_sock = $sock->accept()) { $kid = fork; # Do some stuff exit; } continue { # parent closes the client since # it is not needed close $new_sock; } goto STARTING;
The Client on it's hand is just connecting and disconnecting from this server. My question is: how can I restart the server program (ie killing all kids, and the mother process and then 'exec $0' the same script again). Or even just exit the whole server script (as I can control it via external script so it loops and restarts). Shorter I need a global exit function for example called like this: &exit_everything; Thanks a lot for your value input!

Replies are listed 'Best First'.
Re: Restart Forked Script
by salva (Canon) on Feb 21, 2006 at 09:07 UTC
    See kill docs on perlfunc.
    kill -1, $master_pid;
    will send a signal to the master process and all its children.

    To restart the parent you can use a signal handler:

    $SIG{HUP} = sub { exec @restart_args if $$ == $master_pid; exit };
      How do I determine the PID of the master process ? Also I forget to mention that I am running ActiveState Perl on Win32. I hope this doesn't matter. :)
        Also I forget to mention that I am running ActiveState Perl on Win32. I hope this doesn't matter

        Unfortunately it does!

        Windows does not support fork at the OS level, perl tries to emulate it creating a new threat and cloning your program state but it is far from perfect.

        On Windows it is better to use threads to run several tasks in parallel rather than fork.

Re: Restart Forked Script
by bart (Canon) on Feb 21, 2006 at 14:09 UTC
    You don't make sense to me. You want the server to restart, so you want to kill all running child instances? Why? It's busy doing something useful for some client, I'm quite sure you don't really want to interrupt that. After this one client, it'll just quit, and that shouldn't take too long. So, just let it die off by itself.

    If you just restart the parent, all new connections will get a fork from the new parent.

      The point is that the clients stay permanently connected, and reconnect if the connection is dropped. The server is POS server and the clients are Tills working with the POS server. There is a third program that downloads a new version for the server from time to time, which requires the server to end its work and to restart. That's why what I was thinking about is a shell script / batch file that loops forever and starts the server in each loop and waits until it dies, then again starts it. In such case I just need to exit the master process and the batch script will restart it. May be there is better way, but because it is forked process I can't just use exec $0. Perhaps you can offer a better way of doing it. If it is not a forking script I am just using 'exec $0' and it restarts just perfect.
        Hmm. Pile upon pile of... nevermind. ;-)

        Anyway, since this is for Windows, with emulated fork, I'll just pass you some thougths:

        • fork is emulated using threads... Doesn't that imply that if the parent process quits, that all children will go, too?
        • AFAIK there's no such things as zombie processes on Windows. It just doesn't work that way. Thus wait, and waitpid probably are unnecessary.

        There used to be a fantastic page explaining how fork is supposed to work and how it is emulated on Windows, in Perl, using a comic strip, in Mr. Peabody Explains fork(). The domain (temporarily?) just vanished, according to Google's cache: "This page will be going away on 1/14/2006. This is a planned outage, and will last approximately 90 days." So here's hoping it still will come back, one day...

        Meanwhile, here's what I think that should be a generic solution, even for Unix: instead of just throwing the kids' ids away, just keep track of them yourself: store them in a complex data structure, and keep an eye on them in the REAP routine. I propose using a hash, that's the easiest for lookup. Here's some untested code, adapted from your original:

        my %kids; sub REAP { while((my $deceased = waitpid(-1, WNOHANG)) != -1) { delete $kids{$deceased}; } $SIG{CHLD} = \&REAP; } $SIG{CHLD} = \&REAP; while ($new_sock = $sock->accept()) { if(my $kid = fork) { $kids{$kid} = 1; # parent closes the client since # it is not needed close $new_sock; } else { die unless defined $kid; # if fork failed # the kid's work # ... exit; } }

        To kill them all, just work your way through keys %kids. kill apparently can handle them all without loop.

        delete $SIGNAL, keys %kids;
        I don't know what signal you're supposed to be using.

        Like I said, I don't expect the REAP routine to ever be triggered on Windows, but I'm not sure. In that case, the %kids hash would only grow over time, but probably not at a huge rate, and thus, would not be a real problem.