mod_fcgi、apache 下 PHP 崩溃(段错误)

mod_fcgi、apache 下 PHP 崩溃(段错误)

我一直在使用以下方法编写网站:

  1. Zend Framework 1.11.5(完整 MVC)
  2. PHP 5.3.6
  3. Apache 2.2.19
  4. CentOS 5.6 i686 virtuozzo 在 vps 上
  5. cPanel WHM 11.30.1(版本 4)
  6. Mysql 5.1.56-日志
  7. Mysqli API 5.1.56

问题从这里开始https://stackoverflow.com/questions/6769515/php-programming-seg-fault。简而言之,php 给我带来了随机分段错误。

[Wed Jul 20 17:45:34 2011] [error] mod_fcgid: process /usr/local/cpanel/cgi-sys/php5(11562) exit(communication error), get unexpected signal 11
[Wed Jul 20 17:45:34 2011] [warn] [client 190.78.208.30] (104)Connection reset by peer: mod_fcgid: error reading data from FastCGI server
[Wed Jul 20 17:45:34 2011] [error] [client 190.78.208.30] Premature end of script headers: index.php

关于扩展。当我使用“--enable-debug”标志编译 php 时,我必须禁用此行:

zend_extension="/usr/local/IonCube/ioncube_loader_lin_5.3.so"

否则,服务器不会接受请求,我会收到“与服务器的连接已重置”的消息。出于同样的原因,我可能也必须禁用 eaccelerator。我仍然不明白为什么 apache 有时会运行它,而有时却不会:

extension="eaccelerator.so"

无论如何,在我运行 httpd 后,可能会随机出现段错误。如果我没有使用“--enable-debug”标志编译 php,我可能会确定地遇到 php 崩溃:

<?php
class Admin_DbController extends Controller_BaseController
{
    public function updateSqlDefinitionsAction()
    {
        $db = Zend_Registry::get('db'); 
        $row = $db->fetchRow("SHOW CREATE TABLE 222AFI");
    }
}
?>

但是如果我使用“--enable-debug”标志编译 php,则很难出现此错误。我必须增加一些复杂性才能使其崩溃。我必须在几秒钟内执行许多并行请求才能崩溃:

<?php
class Admin_DbController extends Controller_BaseController
{
    public function updateSqlDefinitionsAction()
    {
        $db = Zend_Registry::get('db');
        $tableList = $db->listTables();
        foreach ($tableList as $tableName){
            $row = $db->fetchRow("SHOW CREATE TABLE " . $db->quoteIdentifier($tableName));
            file_put_contents(
                DB_DEFINITIONS_PATH . '/' . $tableName . '.sql',
                $row['Create Table'] . ';'
            );
        }
    }
}
?>

请注意,这是相同的脚本,但为数据库中的所有表而不是一个表创建 DDL。似乎如果 php 负载过重(带有扩展程序并且我执行了许多并行请求),php 就会崩溃。


关于使用“-X”启动 httpd:我试过了。问题是,使用 --enable-debug 很难让 php 崩溃。使用“-X”选项(仅启用一个子进程)我无法执行并行请求。所以我无法创建正确的调试回溯:https://bugs.php.net/bugs-generating-backtrace.php

我的具体问题是,我该怎么做才能获得核心转储?


root@GWT4 [~]# httpd -V
Server version: Apache/2.2.19 (Unix)
Server built:   Jul 20 2011 19:18:58
Cpanel::Easy::Apache v3.4.2 rev9999
Server's Module Magic Number: 20051115:28
Server loaded:  APR 1.4.5, APR-Util 1.3.12
Compiled using: APR 1.4.5, APR-Util 1.3.12
Architecture:   32-bit
Server MPM:     Prefork
  threaded:     no
    forked:     yes (variable process count)
Server compiled with....
 -D APACHE_MPM_DIR="server/mpm/prefork"
 -D APR_HAS_SENDFILE
 -D APR_HAS_MMAP
 -D APR_HAVE_IPV6 (IPv4-mapped addresses enabled)
 -D APR_USE_SYSVSEM_SERIALIZE
 -D APR_USE_PTHREAD_SERIALIZE
 -D SINGLE_LISTEN_UNSERIALIZED_ACCEPT
 -D APR_HAS_OTHER_CHILD
 -D AP_HAVE_RELIABLE_PIPED_LOGS
 -D DYNAMIC_MODULE_LIMIT=128
 -D HTTPD_ROOT="/usr/local/apache"
 -D SUEXEC_BIN="/usr/local/apache/bin/suexec"
 -D DEFAULT_PIDLOG="logs/httpd.pid"
 -D DEFAULT_SCOREBOARD="logs/apache_runtime_status"
 -D DEFAULT_LOCKFILE="logs/accept.lock"
 -D DEFAULT_ERRORLOG="logs/error_log"
 -D AP_TYPES_CONFIG_FILE="conf/mime.types"
 -D SERVER_CONFIG_FILE="conf/httpd.conf"

答案1

获取核心转储需要将rlimit要获取核心转储的进程的“核心文件大小”设置为非零值。假设您在 FCGI 中运行 PHP,您实际上并不关心 Apache 本身,它不会出现分段错误;您需要运行一个脚本,ulimit -c unlimited然后执行您的 FCGI 服务器。

我喜欢为核心设置一个单独的暂存分区,所以如果它们变得有点大或难以控制,它们不会在其他地方引起磁盘填充问题——设置/proc/sys/kernel/core_pattern为类似的值/var/cores/%e-%p.core

答案2

您必须使用 ioncube_loader 和 eaccelerator 吗?至于 eaccelerator,它们对 5.3.x 的支持充其量也只是参差不齐,而且大多数 5.3 用户都依赖 APC(无论如何,通过 FCGI 也能更好地工作)。

如果您必须使用 eaccelerator,您是否会先执行“make distclean”,然后执行其常设的 INSTALL 指令(仔细检查您加载的 phpize 是否适用于 5.3.6,而不是以前安装的某些残留)?我怀疑这就是您的 coredump 的全部来源,而 Apache 根本不是罪魁祸首。

答案3

我找到了一个临时解决方案。虽然不太美观,但很合适:

公共函数 selectCmd($q){

$charsFrom = array("\\a", "\\t", "\\n", "\\v", "\\f", "\\r", "\\\\", "\\0", "\\\"", "\\\'", "\\b");
$charsTo = array("\a", "\t", "\n", "\v", "\f", "\r", "\\", "\0", "\"", "\'", "\b");

exec('echo ' . escapeshellarg($q) . ' | mysql' .
    ' -h ' . escapeshellarg($this->_config['host']).
    ' -u ' . escapeshellarg($this->_config['username']).
    ' -p' . escapeshellarg($this->_config['password']).
    ' ' . escapeshellarg($this->_config['dbname']), $output);

$colNames = explode("\t", array_shift($output));
foreach ($colNames as &$colName){
    $colName = str_replace($charsFrom, $charsTo, $colName);
}
unset($colName);

$rowSet = array();
foreach ($output as $line){
    $row = array();
    $rawRow = explode("\t", $line);
    for ($i = 0; $i < count($rawRow); ++ $i){
        $row[$colNames[$i]] = str_replace($charsFrom, $charsTo, $rawRow[$i]);
    }
    $rowSet[] = $row;
}
return $rowSet;

}

您需要用真正的连接数组替换 $this->_config。

此脚本运行任何返回行的 SQL 命令。目前,这是我正在采取的解决方案。如果有人愿意主动帮助我,我仍然有源代码。我也是使用 PHPUnit 和 Zend(确定性)时遇到 PHP 段错误的人。我在工作中做这一切,没有资源来测试它。感谢您的理解。

答案4

不确定在这种情况下是否有帮助,但我只设置了一个子项,找到 PID,然后使用 strace 进行调试

strace -p PID

相关内容