机器搬家之后,之前一直稳定的PHP多进程程序子进程突然异常退出,但是退出的不是很频繁,查看进程日志并也没有发现有什么导致退出的,问题比较诡异。于是开启了一段问题排查之路。
首先查看内核日志,使用dmesg,拉到最后发现有一些这样的错误,看来确实是崩溃了。
[4791991.998535] php[16776]: segfault at 7f6443ee18c8 ip 00007f6443ee18c8 sp 00007fff4d4ba818 error 15 in libc-2.17.so[7f6443ee1000+2000] [4792165.192628] php[609]: segfault at 0 ip 000000000075919d sp 00007fff0c6e0578 error 4 in php[400000+94b000] [4792423.164949] traps: php[2337] general protection ip:75919d sp:7fff0c6e0578 error:0 in php[400000+94b000] [4793914.900298] traps: php[589] general protection ip:7576b6 sp:7fff0c6e0460 error:0 in php[400000+94b000] [4794155.124685] php[25418]: segfault at 35007265746c ip 000000000075919d sp 00007fff0c6e0578 error 4 in php[400000+94b000] [4794677.119847] traps: php[2314] general protection ip:75959b sp:7fff4d4ba840 error:0 in php[400000+94b000] [4795121.747090] php[4642]: segfault at 0 ip 000000000075919d sp 00007fff0c6e0578 error 4 in php[400000+94b000] [4795666.787427] php[2372]: segfault at 40 ip 000000000075958c sp 00007fff0c6e0500 error 4 in php[400000+94b000] [4796212.001686] php[6224]: segfault at 10 ip 000000000075919d sp 00007fff0c6e0578 error 4 in php[400000+94b000] [4796224.510583] traps: php[6156] general protection ip:75919d sp:7fff0c6e0578 error:0 in php[400000+94b000] [4796337.623455] php[562]: segfault at 247ec40 ip 000000000247ec40 sp 00007fff0c6e04d8 error 15 [4796427.436886] php[1711]: segfault at ffffffffffffffff ip 00000000007576b6 sp 00007fff0c6e0460 error 5 in php[400000+94b000] [4796554.025960] php[6662]: segfault at 6b6f01000040 ip 000000000075958c sp 00007fff0c6e0500 error 4 in php[400000+94b000] [4797141.552356] php[6658]: segfault at 18 ip 0000000000758daf sp 00007fff0c6e04d0 error 4 in php[400000+94b000] [4797495.302089] php[7239]: segfault at 110 ip 00000000007576d2 sp 00007fff0c6e0460 error 4 in php[400000+94b000] [4797867.446166] php[8265]: segfault at 247e730 ip 000000000247e730 sp 00007fff0c6e04d8 error 15 [4798245.596106] php[8223]: segfault at 247ef40 ip 000000000247ef40 sp 00007fff0c6e04d8 error 15 [4798514.326132] traps: php[8152] general protection ip:75919d sp:7fff0c6e0578 error:0 in php[400000+94b000] [4798769.904337] traps: php[7255] general protection ip:7576d2 sp:7fff0c6e0460 error:0 in php[400000+94b000] [4799427.934198] php[2297]: segfault at 17b57d0 ip 00000000017b57d0 sp 00007fff4d4ba838 error 15 [4800091.548467] php[9826]: segfault at 247ed10 ip 000000000247ed10 sp 00007fff0c6e04d8 error 15 [4800607.342570] php[11239]: segfault at 100000007 ip 000000000075919d sp 00007fff0c6e0578 error 4 in php[400000+94b000] [4800806.439680] php[9796]: segfault at 247ec90 ip 000000000247ec90 sp 00007fff0c6e04d8 error 15 [4801110.909591] php[8317]: segfault at 247ed20 ip 000000000247ed20 sp 00007fff0c6e04d8 error 15 [4801417.477197] php[9326]: segfault at 0 ip 00000000007576d2 sp 00007fff0c6e0460 error 4 in php[400000+94b000]
运气不错,现在居然就有眉目了。上面的信息一般是内存访问越界导致的。现在找一条看看
php[9326]: segfault at 0 ip 00000000007576d2 sp 00007fff0c6e0460 error 4 in php[400000+94b000]
根据网上的资料,error 后面的数字是比较有用的,上面的是4,转换成二进制是100。
bit2: 值为1表示是用户态程序内存访问越界,值为0表示是内核态程序内存访问越界
bit1: 值为1表示是写操作导致内存访问越界,值为0表示是读操作导致内存访问越界
bit0: 值为1表示没有足够的权限访问非法地址的内容,值为0表示访问的非法地址根本没有对应的页面,也就是无效地址
所以含义就是用户态读取得内存地址无效。但是这个信息太笼统。于是还需要进一步调试,这个时候就要用到core dump了。
开启core dump(核心转储)
Core Dump通常是当程序崩溃的时候,操作系统记录下来当前时间程序的内存状态信息。其中还包括处理器寄存器,程序计数器和堆栈指针,存储器管理信息以及其他处理器和操作系统标志和信息。核心转储通常用于帮助诊断和调试计算机程序中的错误。
更多的信息可以去查阅维基百科 https://en.wikipedia.org/wiki/Core_dump
使用ulimit -c unlimited
开启core dump。由于php的核心转储文件比较大,因此建议不要限制core文件的大小,当调试完毕之后关闭coredump之后就好了。
经过一段时间的运行之后,core文件终于生成了。
可以使用下面的命令调试之前生成的core。假设我们已经进入了core文件的目录下面,php安装在/usr/local/php/下面,core文件问core.30983
gdb /usr/local/php/bin/php -c core.30983
之后我们可以看到这么一些信息(关于gdb的使用,可以自行google)
Program terminated with signal 11, Segmentation fault. #0 0x000000000075958c in _php_stream_set_option () Missing separate debuginfos, use: debuginfo-install php55-5.5.6-2.el7.centos.x86_64 (gdb) bt #0 0x000000000075958c in _php_stream_set_option () #1 0x0000000000759648 in _php_stream_eof () #2 0x00007fd73cc1f014 in redis_check_eof (redis_sock=redis_sock@entry=0x2847918, no_throw=no_throw@entry=0) at /tmp/pear/temp/redis/library.c:167 #3 0x00007fd73cc1ef8f in redis_sock_write (redis_sock=redis_sock@entry=0x2847918, cmd=0x281b038 "*4\r\n$5\r\nSETEX\r\n$48\r\nlock::createid::b476a966712ffc251747356512675590\r\n$2\r\n10\r\n$1\r\n1\r\n", sz=85) at /tmp/pear/temp/redis/library.c:1926 #4 0x00007fd73cbf6113 in zim_Redis_setex (ht=3, return_value=0x28af538, return_value_ptr=0x0, this_ptr=0x2820640, return_value_used=1) at /tmp/pear/temp/redis/redis.c:875 #5 0x000000000084e9c8 in zend_do_fcall_common_helper_SPEC () #6 0x00000000007c88d8 in execute_ex () #7 0x00000000007a1130 in zend_execute_scripts () #8 0x0000000000742bfb in php_execute_script () #9 0x0000000000850751 in do_cli () #10 0x0000000000432c7f in main ()
可以看到引起segfault的原因是phpredis的扩展的问题。目前还没有深入php的源代码,不清楚到底是什么导致读取了非法的内存地址,因此只能去搜索这个错误了。在phpredis的github中的issue中看到有很多人遇到了和我一样的问题,原因是pconnect导致的。在常驻内存的脚本中,使用pconnect可能会引起上面的问题。
最终把pconnect改成connect就没问题了。
你可能还喜欢下面这些文章
从程序员的角度来看, Shell本身是一种用C语言编写的程序,从用户的角度来看,Shell是用户与Linux操作系统沟通的桥梁。用户既可以输入命令执行,又可以利用 Shell脚本编程,完成更加复杂的操作。在Linux GUI日益完善的今天,在系统管理等领域,Shell编程仍然起着不可忽视的作用。深入地了解和熟练地掌握Shell编程,是每一个Linux用户的必修 功课之一。Linux的Shell种类众多,常见的有:Bourne Shell(/usr/bin/sh或/bin/sh)、Bourne Again Shell(/bin/bash)、C Shell(/usr/bin/csh)、K Shel
程序员难免要经常画流程图,状态图,时序图等。以前经常用 visio 画,经常为矩形画多大,摆放在哪等问题费脑筋。有时候修改文字后,为了较好的显示效果不得不再去修改图形。今天介绍的工具是如何使用 Sublime + PlantUML 的插件画流程图,状态图,时序图等。这是一种程序员看了就会爱上的画图方式:自然,高效。什么是 PlantUMLPlantUML 是一个画图脚本语言,用它可以快速地画出:时序图流程图用例图状态图组件图简单地讲,我们使用 visio 画图时需要一个一个图去画,但使用 PlantUML 只需要用文字表达出图的内容,然后就可以直接生成图片。看一个最简单的例子:软件安装这些软件
简介awk是一个强大的文本分析工具,相对于grep的查找,sed的编辑,awk在其对数据分析并生成报告时,显得尤为强大。简单来说awk就是把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进行各种分析处理。awk有3个不同版本: awk、nawk和gawk,未作特别说明,一般指gawk,gawk 是 AWK 的 GNU 版本。awk其名称得自于它的创始人 Alfred Aho 、Peter Weinberger 和 Brian Kernighan 姓氏的首个字母。实际上 AWK 的确拥有自己的语言: AWK 程序设计语言 , 三位创建者已将它正式定义为“样式扫描和处理语言”。它允许您
PHP转Go的程序员很多,使用Go重写Web应用,代价不高,并且所带来性能的提升很明显,因此很多PHP程序员正在转Go。PHP是一个弱类型,解释型的语言,Go是一个强类型,编译型语言,两者的差别很大。如果长期使用PHP,使用Go的时候,一些惯性思维会带来不太好的效果。这里总结一些从PHP转到Go需要注意的点。警惕内存越界访问一个数组,在php中,如果a是一个空数组,直接访问a会出现警告,但程序还能继续运行,而在Go中,由于访问一个不存在的地址,程序会直接崩溃。因此Go中需要时刻警惕内存越界。在访问数组下标的时候,如果不能确认需要访问数据一定存在,那么一定要使用len判断数组长度,需要访问的下标
C++中,动态内存管理是通过一对运算符来完成:new 和 delete。new操作符在内存中为对象分配空间并返回一个指向该对象的指针,delete接收一个动态对象的指针,销毁该对象,并释放与之相关的内存。手动管理内存看起来只有这两个操作,似乎很轻松,但实际上这是一件非常繁琐的事情,分配了内存但没有释放内存的场景发生的概率太大了!回想一下,你有多少次打开抽屉却没关上,拿出来的护肤品擦完脸之后却忘了放回去,吃完饭却忘了洗碗。类似这种没有收尾的事情我做的太多了。(以上这些都是在实际生活中我爱人批评我的点)我连这种明面上的事情都能忘记收尾,何况分配内存!所以为了世界和平,我放弃了手动管理内存。好在C+
centos7系统初初始化工作以及网站环境搭建(php7+nginx+mysql)
拿到一台做网站的主机, 我们先要做一些环境初始化的工作, 由于这些工作会有些繁琐,因此记录一下. 后面将这些流程写成一个shell脚本,一次性完成.此次工作流程如下: 安全性设置 额外的目录创建 网站环境搭建安全性设置一般从某云上买的主机, 默认账户是root, 为了不被暴力破解, 我们首先需要设置一个强一点的密码,不过更好的方法是禁用root, 另外创建一个用户来作为日常管理的账户.第一步: 创建一个新的账户,并且能够切换到root权限比如我的用户名叫xiaobai, 添加用户名就是useradd xiaobai设置密码passwd xiaobai之后输入密码,一个新的账户就设定好了.
编译步骤gcc 与 g++ 分别是 gnu 的 c & c++ 编译器。gcc/g++ 在执行编译工作的时候,总共需要4步:预处理,生成 .i 的文件将预处理后的文件转换成汇编语言, 生成文件 .s 有汇编变为目标代码(机器代码)生成 .o 的文件连接目标代码, 生成可执行程序 参数详解-x language filename参数含义为指定文件所使用的语言。根据约定,C语言的后缀名称为".c",而 C++ 的后缀名为".cpp"或".cc",但如果你的源代码后缀不约定的那几种,那么需要使用-x参数来指定文件所使用的语言。这个参数对他后面的文件名都起作用。 可以使用的参数吗有下面的这些:
原文标题:15天玩转redis —— 第十一篇 让你彻底了解RDB存储结构这里我们来继续分析一下RDB文件存储结构,首先大家都知道RDB文件是在redis的“快照”的模式下才会产生,那么如果我们理解了RDB文件的结构,是不是让我们对“快照”模式能做到一个心中有数呢?一:RDB结构剖析首先呢,我们要对RDB文件有一个概念性的认识,比如下面画的图一样: 从图中,我们大概看到了RDB文件的一个简要的存储模式,但为了更好的方便对照,我准备save一个empty database,对比一下看看效果: 然后我们用winHex打开dump.rdb文件,看看它的16进制。好了,该打开的我都
这是我的C++学习笔记第一篇,同所有的程序语言学习路径一样,首先学习的是变量和数据类型。我的学习路径如下:1. 变量和数据类型2. 流程控制3. 函数声明和调用4. 面向对象5. 标准库这一章,学习的是变量和数据类型,需要了解的有:了解这些,对于变量基本就够了。Hello world在开始之前,先写一个hello world来熟悉一下程序的主要结构以及如何打印一个变量。iostream提供标准输入输出的头文件,程序以main函数问入口,std为标准库的命名空间,“<<” 为输出操作符,std::cout为标准输出,std::endl为结束符,表示将等待输出的内容从内存传送到标准输出
布隆过滤器(bloom filter)介绍以及php和redis实现布隆过滤器实现方法
引言在介绍布隆过滤器之前我们首先引入几个场景。场景一在一个高并发的计数系统中,如果一个key没有计数,此时我们应该返回0。但是访问的key不存在,相当于每次访问缓存都不起作用了。那么如何避免频繁访问数量为0的key而导致的缓存被击穿?有人说, 将这个key的值置为0存入缓存不就行了吗?这是确实是一种解决方案。当访问一个不存在的key的时候,设置一个带有过期时间的标志,然后放入缓存。不过这样做的缺点也很明显:浪费内存和无法抵御随机key攻击。场景二在一个黑名单系统中,我们需要设置很多黑名单内容。比如一个邮件系统,我们需要设置黑名单用户,当判断垃圾邮件的时候,要怎么去做。比如爬虫系统,我们要记录下
赞赏微信赞赏支付宝赞赏