(= (PHP require) 引起的 SIGBUS)
sigbus coredump
php中有这么一个问题, 到2017年仍然没有解决。大概是这么一个问题运行下面这句 shell 就可能出现下面的coredump。
for ((n=0;n<100;n++)); do sapi/cli/php test.php & done
test.php 1
2
3
4<?php
$c = '<?php $string = "'. str_repeat('A', mt_rand(1, 256 * 1024)) ."; ?>.\r\n"
file_put_contents(__DIR__ . '/test.tpl', $c);
require_once __DIR__ . '/test.tpl';
1 |
|
问题的原因 nikic 给出了很清晰的解释: 1
2
3
4
5
6
7The issue here seems pretty clear. We are mmap()ing the file. While the file is mapped, it is modified, resulting in an effective ftruncate(). Here is what the man page for ftruncate() has to say on the topic:
If the effect of ftruncate() is to decrease the size of a shared memory object or memory mapped file and whole pages beyond the new end were previously mapped, then the whole pages beyond the new end shall be discarded.
If the Memory Protection option is supported, references to discarded pages shall result in the generation of a SIGBUS signal; otherwise, the result of such references is undefined.
This is precisely what we are observing here.
说白了就是require_once
的test.tpl
这个文件的大小在不停的变化,
从而导致这个问题。具体想深入了解下怎么回事的参考下 tlpi
Chapter 49。
怎么知道系统底层用 mmap引起的?可以用strace 跟踪上面 php
的运行,观察到 require/require_once/include/include_once
底层的系统调用。另外nikic上面也有提示。
sigbus 最小完整问题复现
下面是一段c
代码模拟出现sigbus的情况,其实也是上面这个问题触发sigbus
时,
最小完整问题复现代码。 - 我的系统页面大小为 4k (具体数值 linux 下可用
getconf PAGESIEZE
获得)
1 |
|
总结
如何避免这个 sigbus 引起的 coredump
- 不使用
require/inlude
函数加载大小变化文件。 - 如果非要加载的话,避免文件大小变化的同时加载文件。
- 如果文件大小变化的同时加载文件,保证
页面大小
不属于文件大小变化的范围 (min,max) % 页面大小
。
上面三个建议都可以。