PHP 5.2x中, 由于错误的选用了zend_atoi导致 memory_limit不能设置为超过4G的值.
今天同事分享给我一个问题(thans to yanmi), 一段代码, 设置memory_limit为4096M会导致内存耗尽, 而设置4095M就不会. 奇怪的问题呵.
那是怎么回事呢?
问题的原因也很简单, 在PHPSRC/main.c中定义的memory_limit设置项的处理器OnChangeMemoryLimit中, 使用了zend_atoi来做为字符串数字化, 这个问题在PHP5.3的版本中已经修正(换成了zend_atol):
| 以下是代码片段: static PHP_INI_MH(OnChangeMemoryLimit) { if (new_value) { PG(memory_limit) = zend_atoi(new_value, new_value_length); } else { PG(memory_limit) = 1<<30; /* effectively, no limit */ } return zend_set_memory_limit(PG(memory_limit)); } |
而, 顾名思义么, atoi是转成整形, 4096M是2的32次方, 发生溢出导致结果为0, zend_atoi代码如下:
| 以下是代码片段: ZEND_API int zend_atoi(const char *str, int str_len) { int retval; if (!str_len) { str_len = strlen(str); } retval = strtol(str, NULL, 0); if (str_len>0) { switch (str[str_len-1]) { case ’g’: case ’G’: retval *= 1024; /* break intentionally missing */ case ’m’: case ’M’: retval *= 1024; /* break intentionally missing */ case ’k’: case ’K’: retval *= 1024; break; } } return retval; } |
最后在zend_set_memory_limit的时候, 会错误的设置memory_limit为mm_heap的blok_size, 那结果就肯定远远小与你所预期的4096M了
| 以下是代码片段: .... AG(mm_heap)->limit = (memory_limit >= AG(mm_heap)->block_size) ? memory_limit : AG(mm_heap)->block_size; ... |
PS, 多看别人的代码是有好处的, 今天有学会了intentionally这个单词, ^_^.