
php iconv() 在linux类系统locale设置有问题情况下导致php-fpm exited on signal 11 (SIGSEGV)
起因※
因为服务器系统还是使用的centos7,然后现在是一个完全停止服务状态,国内的镜像点也纷纷关闭了。
也不记得是不是把源改成了现在唯一能用的http://vault.centos.org/7.9.2009/os/$basearch/
然后运行了一下yum update
然后再然后感觉不对劲了,控制台报错:LC_ALL 无法设置成 zh_CN.utf8 了
然后使用各种命令都会显示语言环境设置错误,后面临时把LC_ALL设成C了暂时用着。
现在我的locale就变成了这样(将就用,有大佬知道怎么修复的,一定要告诉我啊):
LANG=en_US.UTF-8
LC_CTYPE="C"
LC_NUMERIC="C"
LC_TIME="C"
LC_COLLATE="C"
LC_MONETARY="C"
LC_MESSAGES="C"
LC_PAPER="C"
LC_NAME="C"
LC_ADDRESS="C"
LC_TELEPHONE="C"
LC_MEASUREMENT="C"
LC_IDENTIFICATION="C"
LC_ALL=C
然后,运行于这服务器上的nginx就开始在一个网站上报502了
分析思路※
nginx报502 ,我都不想去看nginx日志,跟它没关,上游停止服务,我的服务器是nginx → php-fpm →mysql
那上游就是php-fpm了,php报错:

看不到,再看看系统内核的:
shell 运行 dmesg
:
traps: php-fpm[44605] general protection fault ip:7f24ffc00b1e sp:7fff73b004d0 error:0 in GBK.so[7f24ffc00000+1b000]
[244495.783809] php-fpm[33316]: segfault at 88 ip 00007f9358e00b00 sp 00007ffcbf75d0f0 error 4 in GBK.so[7f9358e00000+1b000] likely on CPU 3 (core 1, socket 1)
[244495.783897] Code: 24 40 00 49 89 16 0f 85 cd 1a 00 00 48 8b 44 24 18 4c 8b 68 30 4d 85 ed 74 44 48 89 5c 24 30 4c 89 eb 4c 8b 6c 24 20 0f 1f 00 <48> 8b 7b 08 48 85 ff 74 18 e8 32 fa ff ff 48 8b 7b 18 49 89 e8 4c
哈哈,看不懂啊,看不懂,error 4 ,4转二进制100
bit2: 值为1表示是用户态程序内存访问越界,值为0表示是内核态程序内存访问越界
bit1: 值为1表示是写操作导致内存访问越界,值为0表示是读操作导致内存访问越界
bit0: 值为1表示没有足够的权限访问非法地址的内容,值为0表示访问的非法地址根本没有对应的页面,也就是无效地址
总结:用户态读取的内存地址无效。
到这里再看到那个GBK.so,我心里已经有个大概了,肯定就是我locale设置不对的问题,哪个PHP代码的运行依赖了系统环境。
后面定位到一运行就让Nginx 报502 ,php 日志 exited on signal 11 (SIGSEGV)
的那段PHP代码:
public function encode($string)
{
if (preg_match("/[\x{4e00}-\x{9fa5}]/u", $string)) {
ini_set('memory_limit', '256M');
set_time_limit(120);
$string = preg_replace("/\s/is", "_", $string);
$string = preg_replace("/(|\~|\_|\`|\!|\@|\#|\$|\%|\^|\&|\*|\(|\)|\-|\+|\=|\{|\}|\[|\]|\||\\|\:|\;|\"|\'|\<|\,|\>|\.|\?|\/)/is", "", $string);
$py = "";
$string = iconv('UTF-8', 'GBK', $string);
for ($i = 0; $i < strlen($string); $i++) {
if (ord($string[$i]) > 128) {
$char = $this->asi2py(ord($string[$i]) + ord($string[$i + 1]) * 256);
$py .= $char[0]; //取拼音的第一个字符
// break;
$i++;
} else {
$py .= $string[$i];
}
}
return strtolower($py);
} else {
return $string;
}
}
是的,我们眼睛就盯着带GBK的就对了,$string = iconv('UTF-8', 'GBK', $string);
iconv : phpinfo() 里面显示的好,

glibc都来了,那看来就是问题关键了,下面是网上查询的一些资料总结:
尽管
glibc
与内核不是直接相关的,但它确实依赖于内核提供的一些功能来实现其服务。例如,iconv
函数在转换字符编码时,可能需要访问内核提供的文件系统或内存管理功能。
glibc
的iconv
实现通常与系统的本地化设置(locale)有关。如果你在使用iconv
时遇到问题,可能需要检查系统的locale设置,确保它们是正确的,并且支持你想要使用的字符编码。这可以通过设置环境变量LANG
和LC_ALL
来实现。
OK: 问题确定。
解决问题※
问题发现了,继续使用iconv
我就要修复locale
环境,暂时还没找到办法。
本着发现问题,解决问题,解决不了换掉的宗旨,换iconv
吧,它的替代者mb_convert_encoding
mb_convert_encoding
依赖于PHP的 mbstring
扩展,通常不依赖于系统的本地化设置或特定的系统库。
于是把:$string = iconv('UTF-8', 'GBK', $string);
替换成:$string = mb_convert_encoding($string, 'GBK', 'UTF-8');
再次测试网站,OK,正常了,不报502了,不纠结了,就这样先吧,哪天再把系统给换了吧。