遇到一部分主机运行 php 的时候报 Bus error 错误,直接退出。
# /path/to/php Bus error
于是用 strace 跟踪了一下,看看到底是怎么回事。
# strace /path/to/php ... ... ... open("/path/to/php/lib/php/extensions/no-debug-non-zts-20090626/mongo.so", O_RDONLY) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0`\270\0\0\0\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0644, st_size=32636, ...}) = 0 mmap(NULL, 2309488, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x2b1e8e689000 mprotect(0x2b1e8e6ba000, 2097152, PROT_NONE) = 0 mmap(0x2b1e8e8ba000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x31000) = 0x2b1e8e8ba000 --- SIGBUS (Bus error) @ 0 (0) --- +++ killed by SIGBUS +++
strace 的最后几行如上面所示,进程受到了一个 SIGBUS 信号,看起来是 mmap 引起的。从这几行里看,是 php 在加载 mongo 扩展 mongo.so 时出的错。搜索了一下,mmap 映射内存的时候,在映射超出文件大小范围的内存空间时,可能引发这个 SIGBUS 。就上面的结果看,fstat 的结果表明,文件大小是 32636 字节。而最后那个 mmap 的 offset 参数 0×31000 即 200704,的定位范围直接就超过了文件大小,所以就引发了这个错误。
考虑到这个问题只在部分主机上出现,所以应当不是 php 或者 mongo 扩展本身的 bug 。最后发现是通过 puppet 同步下来的 mongo.so 不完整,比正常的小了很多。重新同步后恢复正常。
由此也可以看出,在加载动态库时是根据 elf 文件中标记的段大小来映射内存的。所以就发生了在文件不完整时 mmap 出错的情况。