当前位置: 移动技术网 > 网络运营>服务器>Linux > win2003下PHP使用preg_match_all导致apache崩溃问题的解决方法

win2003下PHP使用preg_match_all导致apache崩溃问题的解决方法

2019年05月01日  | 移动技术网网络运营  | 我要评论

小编的平台是windows server 2003(32位系统) + apache/2.2.9 (win32) + php/5.2.17,在使用正则表达式 preg_match_all (如 preg_match_all("/ni(.*?)wo/", $html, $matches);)进行分析匹配比较长的字符串 $html 时(大于10万字节,一般用于分析采集回来的网页源码),apache服务器会崩溃自动重启。
    在apache错误日志里有这样的提示:

复制代码 代码如下:
[thu apr 11 18:31:31 2013] [notice] parent: child process exited with status 128 -- restarting.
[thu apr 11 18:31:31 2013] [notice] apache/2.2.9 (win32) php/5.2.17 configured -- resuming normal operations
[thu apr 11 18:31:31 2013] [notice] server built: jun 13 2008 04:04:59
[thu apr 11 18:31:31 2013] [notice] parent: created child process 2964
[thu apr 11 18:31:31 2013] [notice] disabled use of acceptex() winsock2 api
[thu apr 11 18:31:31 2013] [notice] child 2964: child process is running
[thu apr 11 18:31:31 2013] [notice] child 2964: acquired the start mutex.
[thu apr 11 18:31:31 2013] [notice] child 2964: starting 350 worker threads.
[thu apr 11 18:31:31 2013] [notice] child 2964: listening on port 80.

    经过查阅apache官方以及论坛资料后,发现win平台下用正则 preg_match_all 或preg_match 分析比较长的字符串时,导致apache崩溃重启的原因是windows平台下默认分配的线程堆栈空间 threadstacksize 太小导致的。 win32默认只有256kb,而在 linux下默认值是 8m,这就是为什么同样的程序在 linux平台下正常,而在 win平台下不正常的原因。
    根据pcre library的官方说明:256 kb 的堆栈空间对应的pcre.recursion_limit大小应该不超过524。
here is a table of safe values of pcre.recursion_limit for a variety of executable stack sizes:
下面就是一张stacksize和pcre.recursion_limit对应的建议安全值,超过这个数值就极有可能发生堆栈溢出,apache crash:
复制代码 代码如下:
stacksize   pcre.recursion_limit
 64 mb      134217
 32 mb      67108
 16 mb      33554
  8 mb      16777
  4 mb      8388
  2 mb      4194
  1 mb      2097
512 kb      1048
256 kb      524

如果你没有调整堆栈大小,就必须在使用正则的php页面最开头加入:

复制代码 代码如下:
<?php
ini_set("pcre.recursion_limit", "524"); // php default is 100,000.
?>

查看具体的错误可以使用下面的代码:

复制代码 代码如下:
$resultsarray = preg_match_all("/table.*?<a>/isu", $html, $contents);
if ($resultsarray === 0){
echo get_pcre_err();
}
function get_pcre_err(){
        $pcre_err = preg_last_error();  // php 5.2 and above.
        if ($pcre_err === preg_no_error) {
            $msg = 'successful non-match.';
        } else {
            // preg_match error!
            switch ($pcre_err) {
                case preg_internal_error:
                    $msg = 'preg_internal_error';
                    break;
                case preg_backtrack_limit_error:
                    $msg = 'preg_backtrack_limit_error';
                    break;
                case preg_recursion_limit_error:
                    $msg = 'preg_recursion_limit_error';
                    break;
                case preg_bad_utf8_error:
                    $msg = 'preg_bad_utf8_error';
                    break;
                case preg_bad_utf8_offset_error:
                    $msg = 'preg_bad_utf8_offset_error';
                    break;
                default:
                    $msg = 'unrecognized preg error';
                    break;
            }
        }
    return($msg);
}

对于正则的修饰符 isu 说明:

复制代码 代码如下:
i: 表示in-casesensitive,即大小写不敏感
s: pcre_dotall,表示点号可以匹配换行符。
u: 表示pcre_ungreedy,表示非贪婪,相当于perl/python语言的.*?,在匹配过程中,对于.*正则,一有匹配立即执行,而不是等.*搜索了所有字符再一一返回

    在使用正则表达式时,我们应该尽量避免递归调用,递归容易导致堆栈溢出。比如:
/<table((?!<table).)*?<\/a>/isu 就会发生错误,而使用 /<table.*?<\/a>/i 就正常。

    那么如何增加win平台下 threadstacksize 的大小呢? 在apache的配置文件 httpd.conf 里启用 “include conf/extra/httpd-mpm.conf”(删除前面的注释#),然后在 httpd-mpm.conf 文件里的 mpm_winnt_module 配置模块里设置 “threadstacksize 8400000”即可(大约8m)。

复制代码 代码如下:
<ifmodule mpm_winnt_module>
    threadstacksize 8400000
    threadsperchild      200
    maxrequestsperchild    10000
    win32disableacceptex
</ifmodule>

    这里需要注意的是,32位的apache程序只能最多使用大约2gb内存空间! 因此,threadstacksize 和threadsperchild 的值相乘后(8m * 200)不应该超过2g,否则无法启动apache,出现的错误日志如下:
复制代码 代码如下:
[thu apr 11 20:02:45 2013] [crit] (os 8)存储空间不足,无法处理此命令。  : child 4832: _beginthreadex failed. unable to create all worker threads. created 212 of the 220 threads requested with the threadsperchild configuration directive.

    通过上面的提示,小编可以告诉大家的是在我的这台服务器上,当线程堆栈大小设为8m时,我可以设置的线程数最多是212个。

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网