I have the following PHP 5.6.19
code on a Ubuntu 14.04
server. This code simply connects to a MySQL 5.6.28
database, waits a minute, launches another process of itself, then exits.
Note: this is the full script, and it’s purpose is to demonstrate the problem – it doesn’t do anything useful.
class DatabaseConnector { const DB_HOST = 'localhost'; const DB_NAME = 'database1'; const DB_USERNAME = 'root'; const DB_PASSWORD = 'password'; public static $db; public static function Init() { if (DatabaseConnector::$db === null) { DatabaseConnector::$db = new PDO('mysql:host=' . DatabaseConnector::DB_HOST . ';dbname=' . DatabaseConnector::DB_NAME . ';charset=utf8', DatabaseConnector::DB_USERNAME, DatabaseConnector::DB_PASSWORD); } } } $startTime = time(); // ***** Script works fine if this line is removed. DatabaseConnector::Init(); while (true) { // Sleep for 100 ms. usleep(100000); if (time() - $startTime > 60) { $filePath = __FILE__; $cmd = "nohup php $filePath > /tmp/1.log 2>&1 &"; // ***** Script sometimes exits here without opening the process and without errors. $p = popen($cmd, 'r'); pclose($p); exit; } }
I start the first process of the script using nohup php myscript.php > /tmp/1.log 2>&1 &
.
This process loop should go on forever but… based on multiple tests, within a day (but not instantly), the process on the server “disappears” without reason. I discovered that the MySQL
code is causing the popen
code to fail (the script exits without any error or output).
What is happening here?
Notes
- The server runs 24/7.
- Memory is not an issue.
- The database connects correctly.
- The file path does not contain spaces.
- The same problem exists when using
shell_exec
orexec
instead ofpopen
(andpclose
).
I also know that popen
is the line that fails because I did further debugging (not shown above) by logging to a file at certain points in the script.
Advertisement
Answer
Is the parent process definitely exiting after forking? I had thought pclose
would wait for the child to exit before returning.
If it isn’t exiting, I’d speculate that because the mySQL connection is never closed, you’re eventually hitting its connection limit (or some other limit) as you spawn the tree of child processes.
Edit 1
I’ve just tried to replicate this. I altered your script to fork every half-second, rather than every minute, and was able to kill it off within about 10 minutes.
It looks like the the repeat creation of child processes is generating ever more FDs, until eventually it can’t have any more:
$ lsof | grep type=STREAM | wc -l 240 $ lsof | grep type=STREAM | wc -l 242 ... $ lsof | grep type=STREAM | wc -l 425 $ lsof | grep type=STREAM | wc -l 428 ...
And that’s because the child’s inheriting the parent’s FDs (in this case for the mySQL connection) when it forks.
If you close the mySQL connection before popen
with (in your case):
DatabaseConnector::$db = null;
The problem will hopefully go away.