我在 Ubuntu 14.04
服务器上有以下 PHP 5.6.19
代码。此代码只是连接到 MySQL 5.6.28
数据库,稍等片刻,启动自身的另一个进程,然后退出。
注意:这是完整的脚本,目的是演示问题 - 它没有做任何有用的事情。
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;
}
}
我使用 nohup php myscript.php >/tmp/1.log 2>&1 &
启动脚本的第一个进程。
这个进程循环应该永远持续下去,但是......基于多次测试,在一天之内(但不是立即),服务器上的进程无故“消失”。我发现 MySQL
代码导致 popen
代码失败(脚本退出时没有任何错误或输出)。
这里发生了什么?
注释
- 服务器 24/7 全天候运行。
- 内存不是问题。
- 数据库连接正确。
- 文件路径不包含空格。
- 在使用
shell_exec
或exec
而不是popen
(和pclose
)时存在同样的问题。<
我也知道 popen
是失败的行,因为我通过在脚本中的某些点记录到文件来进一步调试(上面未显示)。
最佳答案
fork后父进程肯定退出了吗?我原以为 pclose
会等 child 退出再返回。
如果它没有退出,我推测因为 mySQL 连接永远不会关闭,所以当您生成子进程树时,您最终会达到它的连接限制(或其他一些限制)。
编辑 1
我刚刚尝试复制这个。我将您的脚本更改为每半秒而不是每分钟 fork 一次,并且能够在大约 10 分钟内将其终止。
看起来子进程的重复创建正在生成越来越多的 FD,直到最终它不能再有:
$ 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
...
那是因为子进程在 fork 时继承了父进程的 FD(在本例中为 mySQL 连接)。
如果您在 popen
之前关闭 mySQL 连接(在您的情况下):
DatabaseConnector::$db = null;
这个问题有望消失。
关于php - MySQL 代码导致 PHP 脚本在 popen/exec 崩溃,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36998023/