好好学习,天天向上,自学网欢迎您!
当前位置:首页 >  考试 >  计算机类 > 内容页

PHP 死锁成绩剖析

2021-09-12 19:02:09计算机类访问手机版412

  php 死锁成绩剖析

  顺序员们面对PHP死锁成绩的时候大概会不重视,认为重启一下就好,但是其实这样是没有办理成绩的。以下是小编精心为大师收拾整顿的PHP 死锁成绩剖析,但愿对大师有所帮忙!更多内容请存眷应届结业生网!

   布景:关于死锁的成绩,人们常常想到呈现一些关于拜访很迟钝,有白页现象,要是测试环境我就真实遇到测试环境有本文谈及一样的成绩你也就重启一下PHP的php-fpm过程发明又好了,隔一段时间又出类似的成绩,你会看下日志,你会发明有很多日志是“Max execution timeout of 60 seconds exceeded”,你会发明这多是一些php的保护过程招致的,你为懂得决测试环境的成绩,于是感到该当把阿谁php-fpm的过程数开多点,大概会好一些,于是你开多了,不断没有面对这个成绩的缘故原由,为何呢,因为公司装PHP的是运维装的,你没有方法或时间去装一个debug版本的php,你说这个成绩让运维的人来查,你感到能查出来?So,这个成绩一拖再拖,但就是没办理,但是有一天你发明磁盘满了,用du去看整体时发明满了,但是假如一个个目录去看发明并无占用几多,也万万没有想到PHP的死锁还会招致磁盘空间占用太多,上面这种环境我就真实遇到过,后来从头reboot操纵系统,磁盘又返来了,所以,我认为是一篇好文章,所以转了此文,也想阐明关于PHP的扩展这方面代码质量把关必要严格,再就是PHP自身关于锁这块要弱化除开cookie/session和cache锁外,别的能不必就不必,尽量罕用锁,这是博主一点鄙视法,下面言归正传。

  发明成绩

  近期发明线上很多呆板的磁盘空间报警, 且日志文件曾经清理,但是磁盘空间没有释放。经由过程ps aux | grep php-cgi 发明, 很多过程的启动时间在几天到几周甚至几个月之前。我们线上的php-cgi都有最大执行次数的。一样平常在1天内城市重启一次。初步结论,这些cgi过程有成绩。

  经由过程lsof -p [pid] 发明, 启动时间好久的cgi过程中翻开了一些日志文件句柄,而且没有封闭。这些日志文件在文件系统中曾经删除了。但是句柄没封闭,招致磁盘空间没有释放。到此,磁盘空间非常的成绩根本断定。是由于cgi没有封闭文件句柄酿成的。

  进一步剖析过程, strace -p [pid], 发明所有非常的过程都堵塞与 fmutex 状态。换句话所,非常的cgi过程死锁了。过程死锁招致翻开的文件句柄没有封闭,所以招致磁盘空间非常。

  为何cgi过程会死锁呢?

  什么是死锁

  学过操纵系统的通同学,都懂得多线程的观点。在多线程中拜访大众资源,必要对资源加锁。拜访结束后,释放锁。假如没有释放锁,那么下一个线程来获得资源的时候就会永远都无法获得资源的锁,于是这个线程死锁了。那么CGI是多线程的大众资源拜访招致的死锁吗? 答案是NO。

  1. CGI 是单线程过程,经由过程ps 就可以看到。过程状态 Sl的才是多线程过程。

  2. 即便是多线程的,死锁发作在PHP的shutdown过程当中调用glibc 中time 函数的地位,不是php模块酿成的。而glibc 中的time相关函数是线程平安的,不会发生死锁。

  那是什么招致的死锁呢?

  经由过程剖析linux中死锁发生的机制,发明除了多线程会发生死锁外,旌旗灯号处置函数同样会发生死锁。那么cgi是由于旌旗灯号处置招致的死锁吗?在这之前介绍一个感念。

  函数的可重入性与旌旗灯号平安

  函数可重入是指,不管第几回进入该函数,函数都能正常执行并返回成果。那么线程平安函数是可重入的吗?答案是NO。 线程平安函数,在第一次拜访大众资源时,会获得全局锁。假如函数没有执行完成,锁还没释放,此时过程被间断。那么在间断处置函数中,再次拜访该函数,就会发生死锁。那么什么样的函数才可以在间断处置函数中拜访呢? 除了没有使用全局锁的函数,还有一些signal safe的系统调用可使用。调用任何其他的非signal safe的函数城市发生不成预知的成果比方 死锁。 详见 man signal。在剖析死锁的缘故原由前,我们先看看cgi执行的流程,剖析此中有无发生死锁的大概。

  PHP-CGI的执行流程

  Glibc中的时间函数使用到了全局锁,包管函数的线程平安,但没有包管旌旗灯号平安signal safe。颠末之前的剖析,我们初步猜忌死锁是由于PHP-CGI过程接纳到了一个旌旗灯号,然后在signal handle中执行了非signal safe的函数。主流程在间断前,正在执行glibc中的.时间函数。在函数获得的锁没释放前,进入间断流程。而间断过程当中又拜访了glibc中的时间函数。于是招致了死锁。

  PHP-CGI的执行流程,如下图所示:

  进一步剖析发明,所有死锁的cgi过程的sapi_global中都记实了一个过错信息

  “Max execution timeout of 60 seconds exceeded”.

  60s 是我们php-cgi中设置执行超时。所以我们确认了,cig在执行过程当中确实发生了超时非常,然后由于longjmp进入了shutdown过程。在shutdown过程当中拜访了glibc中的时间函数。招致了死锁。

  void zend_set_timeoutlong seconds

  TSRMLS_FETCH;

  EGtimeout_seconds = seconds;

  if!seconds

  return;

  ……

  setitimerITIMER_PROF, &t_r, NULL;

  signalSIGPROF, zend_timeout; // 此处会调用zend非常处置函数

  sigemptyset&sigset;

  sigaddset&sigset, SIGPROF;

  ……

  经由过程gdb调试发明,所有PHP-CGI都堵塞在zend_request_shutdown中。zend_request_shutdown会调用用户自界说的php脚本中实现的shutdown函数。假如CGI执行超市,那么按时器会发生SIGPROF旌旗灯号使执行流程间断。假如此时脚本刚好处于调用时间函数的状态,且尚未释放锁资源。然后执行流程进入了 timeout 函数,持续跳转到zend_request_shutdown。此时假如自界说的shutdown函数中拜访了时间函数。就会发生死锁。我们从代码中找到了证据:

  register_shutdown_function 'SimpleWebSvc:: shutdown’;

  我们在php代码中使用qalarm系统,qalarm系统会在cgi执行结束shutdown的时候,注入一个钩子函数,来剖析cgi执行是不是正常,假如不正常,则发送报警信息。而刚好qalarm的报警处置函数中拜访了时间函数。于是就有必定的几率发生死锁。

  结论

  经由过程上面的剖析,我们找到了cgi死锁发生的缘故原由,是应为在signal handler中使用了非signal safe的函数,招致了死锁。

  办理方法

  去掉或简化qalarm注册到shutdown中的钩子函数。防止不平安的函数调用。

TAG标签: 死锁 分析 PHP