MySQL代码导致PHP脚本在popen/exec上崩溃
我在MySQL代码导致PHP脚本在popen/exec上崩溃,php,mysql,linux,ubuntu,pdo,Php,Mysql,Linux,Ubuntu,Pdo,我在ubuntu14.04服务器上有以下php5.6.19代码。这段代码只是连接到一个MySQL 5.6.28数据库,等待一分钟,启动自己的另一个进程,然后退出 注意:这是完整的脚本,它的目的是演示问题-它没有做任何有用的事情 class DatabaseConnector { const DB_HOST = 'localhost'; const DB_NAME = 'database1'; const DB_USERNAME = 'root'; const DB
ubuntu14.04
服务器上有以下php5.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
代码失败(脚本退出时没有任何错误或输出)
这里发生了什么
注释
- 服务器全天候运行
- 记忆不是问题
- 数据库连接正确
- 文件路径不包含空格
- 使用
或shell\u exec
而不是exec
(和popen
)时也存在同样的问题pclose
我还知道,
popen
是一行失败的代码,因为我通过在脚本中的某些点登录到文件进行了进一步调试(上面没有显示)。分叉后父进程肯定会退出吗?我原以为pclose
会等到孩子退出后再回来
如果它没有退出,我会推测,因为mySQL连接从未关闭,所以在生成子进程树时,您最终会达到其连接限制(或其他限制)
编辑1
我只是想复制一下。我将你的脚本改为每半秒一次,而不是每分钟一次,并且能够在大约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
...
这是因为在分叉时,子节点继承了父节点的FDs(在本例中是mySQL连接)
如果在popen
之前关闭mySQL连接(在您的情况下):
这个问题有望解决
脚本退出时没有任何错误或输出
当代码中没有错误检查时,这并不奇怪。然而,如果它真的是“崩溃”,那么:
- 如果原因被PHP运行时捕获,那么它将尝试记录错误。您是否尝试过谨慎地创建一个错误场景,以确定重新排序/日志记录是否按预期工作
- 如果PHP运行时没有捕获错误,那么操作系统应该转储一个corefile,是吗?查找核心文件
exit
时PHP应该这样做)
您可能需要阅读,我在使用和MySQL连接时遇到了类似的情况。这里的原因可能是相同的 背景信息 创建一个子进程。对的调用关闭通信通道,子进程继续运行,直到退出。这是事情开始失控的时候 当子进程完成时,父进程将收到一条消息。这里的父进程是运行您发布的代码的PHP解释器。子进程是使用
popen()
启动的进程(不管运行什么命令)
这里有一件小事情您可能不知道,或者您在文档中找到了,并忽略了它,因为当您使用PHP编程时,它没有多大意义。以下文件中提到了这一点:
如果调用被信号中断,sleep()
返回一个非零值
sleep()
PHP文档中未说明的内容在系统调用文档中有明确说明:
sleep()
回到你的代码。
PHP解释器在代码中有两个地方调用usleep()
Linux系统函数。其中一个是清晰可见的:PHP代码调用它。另一个是隐藏的(见下文)
发生了什么(可见部分)
从第二次迭代开始,如果一个子进程(在上一次迭代中使用popen()
创建)在父程序位于usleep(100000)
调用中时恰好退出,PHP解释器进程将收到sigchd
信号,并在超时之前恢复执行。usleep()
返回的时间早于预期。由于超时时间较短,肉眼无法观察到这种效果。用10秒代替0.1秒,你会注意到的
但是,除了中断的超时之外,这不会以致命的方式影响代码的执行
为什么会崩溃(看不见的部分)
第二个输入信号影响程序执行的地方隐藏在PHP解释器代码的深处。出于某些协议原因,MySQL客户端库使用DatabaseConnector::$db = null;
pcntl_sigprocmask(SIG_BLOCK, array(SIGCHLD));
pcntl_wait($status, WNOHANG);