memory_limit的一个bug | 风雪之隅

原文:memory_limit的一个bug | 风雪之隅

27 Nov 09 memory_limit的一个bug




PHP 5.2x中, 由于错误的选用了zend_atoi, 导致memory_limit不能设置为超过4G的值.


今天同事分享给我一个问题(thans to yanmi), 一段代码(PHP 5.2.11 Linux/X86_64),设置memory_limit为4096M会导致内存耗尽, 而设置4095M就不会. 奇怪的问题呵.


那是怎么回事呢?


问题的原因也很简单, 在PHPSRC/main.c中定义的memory_limit设置项的处理器OnChangeMemoryLimit中, 并没有检测当前的机器字长, 而统一使用了zend_atoi来做为字符串数字化, 这个问题在PHP5.3的版本中已经修正(换成了zend_atol):

  1. static PHP_INI_MH(OnChangeMemoryLimit)
  2. {
  3. if (new_value) {
  4. PG(memory_limit) = zend_atoi(new_value, new_value_length);
  5. } else {
  6. PG(memory_limit) = 1<<30; /* effectively, no limit */
  7. }
  8. return zend_set_memory_limit(PG(memory_limit));
  9. }

而, 顾名思义么, atoi是转成整形, 4096M是2的32次方, 发生溢出, 继而环绕成结果为0, zend_atoi代码如下:

  1. ZEND_API int zend_atoi(const char *str, int str_len)
  2. {
  3. int retval;
  4. if (!str_len) {
  5. str_len = strlen(str);
  6. }
  7. retval = strtol(str, NULL, 0);
  8. if (str_len>0) {
  9. switch (str[str_len-1]) {
  10. case 'g':
  11. case 'G':
  12. retval *= 1024;
  13. /* break intentionally missing */
  14. case 'm':
  15. case 'M':
  16. retval *= 1024;
  17. /* break intentionally missing */
  18. case 'k':
  19. case 'K':
  20. retval *= 1024;
  21. break;
  22. }
  23. }
  24. return retval;
  25. }

最后在zend_set_memory_limit的时候, 会错误的设置memory_limit为mm_heap的blok_size, 那结果就肯定远远小与你所预期的4096M了

  1. ....
  2. AG(mm_heap)->limit = (memory_limit >= AG(mm_heap)->block_size) ? memory_limit : AG(mm_heap)->block_size;
  3. ...

最后, 如果是32位的机器, 那确实不算bug, 但现在的机器很多都64了, 最大内存也不再是4GB了, PHP也要与时俱进啊.


PS, 多看别人的代码是有好处的, 今天有学会了intentionally这个单词, ^_^.




分享到: 1

document.getElementById("bdshell_js").src = "http://bdimg.share.baidu.com/static/js/shell_v2.js?t=" + new Date().getHours();



Related Posts:



Tags: PHP, php bug, php源码分析



Filed in PHP源码分析
上一篇:Javascript手记-执行环境和作用域


下一篇:Angular实战之使用NG-ZORRO创建一个企业级中后台框架