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页面最开头加入: ini_set("pcre.recursion_limit","524"); // PHP default is 100,000. ?> 查看具体的错误可以使用下面的代码: $resultsArray = preg_match_all("/table.*?/isU",$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语言的.*?,在匹配过程中,对于.*正则,一有匹配立即执行,而不是等.*搜索了所有字符再一一返回 在使用正则表达式时,我们应该尽量避免递归调用,递归容易导致堆栈溢出。比如: / 相关内容
|
