Created
June 11, 2019 16:35
-
-
Save spajak/eb30af9488bb89ab0ebdfa3f5f496174 to your computer and use it in GitHub Desktop.
Forking in PHP 7 done right. No zombies!!
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Forking in PHP 7 done right. Requires PHP >= 7.1 and Process Control extension (pcntl) | |
* | |
* @author Sebastian Pająk | |
* Good article in the subject that inspired me: https://ruslanspivak.com/lsbaws-part3 | |
*/ | |
// Enable asynchronous signal handling (as of PHP 7.1) | |
pcntl_async_signals(true); | |
// How many children to fork | |
define('MAX_CHILDREN', 10); | |
function console($message) { | |
file_put_contents('php://stdout', $message."\n"); | |
} | |
// Function that gets called in a forked process | |
function childProcess() { | |
$pid = getmypid(); | |
console(" We are now in a child process. PID: $pid"); | |
sleep(5); | |
console(" Finishing child process. PID: $pid"); | |
} | |
// Register handler for signal that is sent to a parent process whenever | |
// one of its child processes terminates or stops. | |
$result = pcntl_signal(SIGCHLD, function($signo, $siginfo) { | |
// Killing zombies :) | |
console("Got SIGCHLD signal"); | |
while (0 != $pid = pcntl_wait($status, WNOHANG)) { | |
if ($pid == -1) return; | |
} | |
}); | |
if (false === $result) { | |
throw new RuntimeException('Could not register SIGCHLD signal!'); | |
} | |
// Register handler for signal that is sent when the user types the INTR character | |
// (normally Ctrl+c). | |
$result = pcntl_signal(SIGINT, function($signo, $siginfo) { | |
console("Got SIGINT signal"); | |
console("Waiting for children to finish..."); | |
while (0 < pcntl_wait($status, WNOHANG)) { | |
usleep(20000); | |
} | |
console("Stopping master process"); | |
// Restore default handlers. Probably not needed here as we are dying anyway. | |
// But it may be used in other situations. | |
pcntl_signal(SIGCHLD, SIG_DFL); | |
pcntl_signal(SIGINT, SIG_DFL); | |
exit(0); | |
}); | |
if (false === $result) { | |
throw new RuntimeException('Could not register SIGINT signal!'); | |
} | |
console("Starting master process"); | |
$count = 0; | |
// Master loop. Creates a new child every 1 sec | |
while ($count < MAX_CHILDREN) { | |
if (-1 === $pid = pcntl_fork()) { | |
throw new RuntimeException('Could not fork!'); | |
} | |
if (0 === $pid) { // <=== If true then we are inside a forked process | |
childProcess(); | |
exit(0); | |
} | |
$count += 1; | |
sleep(1); | |
} | |
console("Master process has done his job. Sleeping..."); | |
while (true) { | |
sleep(1); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment