浅谈php异常终止 exited on signal 11 (SIGSEGV) 与 iconv() 的关系

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 函数在转换字符编码时,可能需要访问内核提供的文件系统或内存管理功能。

glibciconv 实现通常与系统的本地化设置(locale)有关。如果你在使用 iconv 时遇到问题,可能需要检查系统的locale设置,确保它们是正确的,并且支持你想要使用的字符编码。这可以通过设置环境变量 LANGLC_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了,不纠结了,就这样先吧,哪天再把系统给换了吧。

“您的支持是我持续分享的动力”

微信收款码
微信
支付宝收款码
支付宝

目录