Hi All,

Was hoping for a little guidance from those who've been there and done that in regards to forking processes and controlling the resulting zombies !.

I have a requirement to implement a daemon on 12+ servers that will listen on a socket for a given notification to do a particular task(move a given file from one server to another). Each server currently handles about 20K requests daily at present using another method, but it is beginning to break under the load. We need this to scale to over 200K / day.

The below code is a cut and paste of various sources on howto's for socket servers, and pcntl. Initial testing seems positive, but all connections to the socket server ( each connection generates a new fork) do not exit nicely, they just zombie. I'm going crazy...and I'm sure its just something simple that someone knowledgeable will point out in a few minutes.

any help is GREATLY appreciated. below is the test code.

<?php
declare(ticks=1);

function sig_handler($signo) 
{
syslog(LOG_ALERT, "in sig handler now signo is $signo");
     switch ($signo) {
         case SIGTERM:
             // handle shutdown tasks
             exit;
             break;
         case SIGHUP:
             // handle restart tasks
             break;
         default:
         	 exit();
             // handle all other signals
     }

}
// setup signal handlers
pcntl_signal(SIGTERM, "sig_handler");
pcntl_signal(SIGHUP, "sig_handler");

$pid = pcntl_fork();
if ($pid == -1) {
     die("could not fork"); 
} else if ($pid) {
     exit(); // we are the parent 
} else {
     // we are the child
}

// detatch from the controlling terminal
if (!posix_setsid()) {
   die("could not detach from terminal");
}

/* Turn on implicit output flushing so we see what we're getting
* as it comes in. */
//ob_implicit_flush();

$address = '10.10.10.7';
$port = 10000;

if (($sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) < 0) {
	echo "socket_create() failed: reason: " . socket_strerror($sock) . "\n";
}

if (($ret = socket_bind($sock, $address, $port)) < 0) {
	echo "socket_bind() failed: reason: " . socket_strerror($ret) . "\n";
}

if (($ret = socket_listen($sock, 5)) < 0) {
	echo "socket_listen() failed: reason: " . socket_strerror($ret) . "\n";
}
do {

	if (($msgsock = socket_accept($sock)) < 0) {
		echo "socket_accept() failed: reason: " . socket_strerror($msgsock) . "\n";
		break;
	}
	syslog(LOG_ALERT, "incoming connection on port 10000 detected !..forking..");

	$subpid = pcntl_fork();
	if ($subpid != 0) {
		$return = pcntl_waitpid($subpid, $status, WNOHANG);
		syslog(LOG_ALERT, "pcntl waitpid of $subpid returned ".$return);
		continue;
	} 
	syslog(LOG_ALERT, "forked pid is ".posix_getpid());


	/* Send instructions. */
	$msg = "\nWelcome to the PHP Test Server. \n" .
				"To quit, type 'quit'. To shut down the server type 'shutdown'.\n";
				socket_write($msgsock, $msg, strlen($msg));

				do {
					if (false === ($buf = socket_read($msgsock, 2048, PHP_NORMAL_READ))) {
						syslog(LOG_ALERT, "socket_read() failed: reason: " . socket_strerror($ret) . "\n");
						break 2;
					}
					if (!$buf = trim($buf)) {
						continue;
					}
					if ($buf == 'quit') {
						break;
					}
					if ($buf == 'shutdown') {
						socket_close($client[$i]['sock']);
						break 2;
					}
					$talkback = "PHP: You said '$buf'.\n";
					socket_write($msgsock, $talkback, strlen($talkback));
					//echo "$buf\n";
				} while (true);
				socket_close($msgsock);

	syslog(LOG_ALERT, "child now exiting? ");
	posix_kill(posix_getpid(), SIGTERM); 

} while (true);

socket_close($sock);
?>

    not to be a downer, but if you have a project that is mission critical and needs to scale well under heavy load, perhaps you need to crack open vi and gcc and leave php for some other projects...

    i am curious as to why you decided to use php ... please tell, was it just show it could be done, or is there a specific reason why

      I haven't done this in PHP, but in perl you have to make sure to call exit (see code below). Unbelievably, my first forking perl script (pun intended) doesn't leave any zombies and I use this subroutine to spawn the long-running child processes (about 5-15 minute long processes). This is pretty much copy/paste out of perl cook book, is there similar section in php cookbook?

      sub spawn {
      
      my $host = shift;
      if( !$host ){
      return;
      }
      #fork, then exec
      my $pid;
      
      if ($pid = fork) {
          # parent here
          # child process pid is available in $pid
      } elsif (defined $pid) { # $pid is zero here if defined
          # child here
          # parent process pid is available with getppid
          my @args = ( $host );
          exec( "my_cool_script.pl", @args );
          exit;
      } elsif ($! =~ /No more process/) {     
          # EAGAIN, supposedly recoverable fork error
          #sleep 5;
          #print "trying to fork again\n";
          #redo FORK;
      } else {
          # weird fork error
          die "Can't fork: $!\n";
      }
      }
      

        I haven't done much forking (at least not with PHP), but I noticed in the example code given on the [man]pcntl_fork[/man] page the original poster's code was based on has the line

             pcntl_wait($status); //Protect against Zombie children

        But yeah, PHP does sound like an overweight solution to this task.

          Write a Reply...