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

小编的平台是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个。

(0)

相关推荐

  • PHP函数preg_match_all正则表达式的基本使用详细解析

    了解正则表达式之前,须要掌握一些常用的正则表达式的基础知识,这些如果能记得最好记得,记不住须要用的时候能查到就行,就多个特殊字符,所以说正则表达式玩的就是特殊,具体大家可以查看更加细致的说明. preg_match_all函数具体说明大家可以查看PHP手册,本文运用 preg_match_all用于测试正则表达的效果. 实例代码: 复制代码 代码如下: $html = '<div id="biuuu">jb51.net</div><div id="

  • PHP preg_match的匹配多国语言的技巧

    正则:[\S]{2,32} 过滤是管用的PHP中: 复制代码 代码如下: <?phpvar_dump( preg_match("/[\S\b]{2,32}/",'ج') );echo '<hr />';var_dump( preg_match("/[\S\b]{2,32}/",'中国') ); 是不行的 需要加一个参数u,按照uniode来读码 复制代码 代码如下: <?phpvar_dump( preg_match("/[\S\b

  • php preg_match_all结合str_replace替换内容中所有img

    采集回来的图片img标签中,有好多javascript脚本和无用的信息,必需过替换自己想要的,比如alt.先看看要过滤的内容,我随便复制出来: 复制代码 代码如下: sdfsdfsdf<img alt="3568df.com靓图" src="http://www.aaa.com/upimg /080330/120D1232295023X0.gif" src="http://www.eee.com/upimg/080330 /120D123229502

  • php中preg_match的isU代表什么意思

    isU是大小写分的意思,这里s还有则不包括换行符而U是反转了匹配数量的值使其不是默认的重复,大概就是这样了个体我们看文章. 正则后面的/(.*)/isU  ,"isU"参数代表什么意思? 这是正则中的修正符. i是同时查找大小写字母, s是圆点(.)匹配所有字符,包括换行符.如果没有设定s,则不包括换行符. U是反转了匹配数量的值使其不是默认的重复,而变成在后面跟上"?"才变得重复 例 preg_match 兼容的正则表达式语法中 b 代表单词边界 所以:下面应该是

  • PHP preg match正则表达式函数的操作实例

    在php中preg_match()函数是用来执行正则表达式的一个常用的函数.正则表达式几乎在所有编程语言里面都会用到,本实例介绍php中正则表达式preg_match函数的应用. preg_match() 函数用于进行正则表达式匹配,成功返回 1 ,否则返回 0 . preg_match() 匹配成功一次后就会停止匹配,如果要实现全部结果的匹配,则需使用 preg_match_all() 函数. 语法: preg_match (pattern , subject, matches) 参数 描述

  • PHP的preg_match匹配字符串长度问题解决方法

    项目中,用preg_match正则提取目标内容,死活有问题,代码测得死去活来. 后来怀疑PHP 的preg_match有字符串长度限制,果然,发现"pcre.backtrack_limit "的值默认只设了100000. 解决办法:ini_set('pcre.backtrack_limit', 999999999); 注:这个参数在php 5.2.0版本之后可用. 另外说说关于:pcre.recursion_limit pcre.recursion_limit是PCRE的递归限制,这个

  • php小经验:解析preg_match与preg_match_all 函数

    正则表达式在 PHP 中的应用在 PHP 应用中,正则表达式主要用于:•正则匹配:根据正则表达式匹配相应的内容•正则替换:根据正则表达式匹配内容并替换•正则分割:根据正则表达式分割字符串在 PHP 中有两类正则表达式函数,一类是 Perl 兼容正则表达式函数,一类是 POSIX 扩展正则表达式函数.二者差别不大,而且推荐使用Perl 兼容正则表达式函数,因此下文都是以 Perl 兼容正则表达式函数为例子说明.定界符Perl 兼容模式的正则表达式函数,其正则表达式需要写在定界符中.任何不是字母.数

  • PHP中preg_match正则匹配中的/u、/i、/s含义

    PHP中preg_match正则匹配的/u /i  /s是什么意思 /u 表示按unicode(utf-8)匹配(主要针对多字节比如汉字) /i 表示不区分大小写(如果表达式里面有 a, 那么 A 也是匹配对象) /s 表示将字符串视为单行来匹配

  • PHP 正则表达式之正则处理函数小结(preg_match,preg_match_all,preg_replace,preg_split)

    前面我们已经学习了正则表达式的基础语法,包括了定界符.原子.元字符和模式修正 符.实际上正则表达式想要起作用的话,就必须借用正则表达式处理函数.本节我们就来介绍一下PHP中基于perl的正则表达式处理函数,主要包含了分割, 匹配,查找,替换等等处理操作,依旧是配合示例讲解,让我们开始吧. 和正则表达式一样,正则表达式处理函数不能够独立使用,而这必须相结合,才能够完成特定的功能.在前面我们也说过,基于perl的正则表达式要快于POXIS正则表达式处理函数,所以我们只介绍以preg开头的基于perl

  • PHP中preg_match函数正则匹配的字符串长度问题

    项目中,用preg_match正则提取目标内容,死活有问题,代码测得死去活来. 后来怀疑PHP 的preg_match有字符串长度限制,果然,发现"pcre.backtrack_limit "的值默认只设了100000. 解决办法:ini_set('pcre.backtrack_limit', 999999999); 注:这个参数在php 5.2.0版本之后可用. 另外说说关于:pcre.recursion_limit pcre.recursion_limit是PCRE的递归限制,这个

  • php中使用preg_match_all匹配文章中的图片

    preg_match_all 函数: int preg_match_all ( string pattern, string subject, array matches [, int flags] )执行一个全局正则表达式匹配在 subject 中搜索所有与 pattern 给出的正则表达式匹配的内容并将结果以 flags 指定的顺序放到 matches 中.     搜索到第一个匹配项之后,接下来的搜索从上一个匹配项末尾开始.   flags 可以是下列标记的组合(注意把 PREG_PATT

随机推荐