• pcntl_fork 引起的奇怪 bug


    最近随手写了个获取软件最新版本号的程序,但是在处理多进程并发的时候遇到了问题。我想在用户请求的时,输出已经保存的版本号,同时异步抓取最新的版本号。
    数据存储用的 redis,在父进程中,与 redis 的通讯没有任何问题,但是在子进程中,与 redis 的通讯就会出现问题,会报类似PHP Notice: Redis::setex(): send of 47 bytes failed with errno=32 Broken pipe in /data/root/api/lib/db.php on line 44
    的错误。
    搜索后发现,有人遇到了同样的 bug(http://stackoverflow.com/questions/23713480/after-php-upgrade-pcntl-fork-causing-errno-32-broken-pipehttps://github.com/phpredis/phpredis/issues/474),他测试了与 mysql 的通讯,在子进程中并没有问题,怀疑是 redis 的 bug。
    既然是子进程的通讯出现了问题,那么在 pcntl_fork
    前关闭连接(只有父进程)、在 pcntl_fork
    之后关闭连接(父进程和子进程)是否可行呢?测试后发现,果然没再报错,第一个问题解决。

    在 php-fpm 中,默认情况下(php-fpm.ini 的默认设置),一个进程(包括 pcntl_fork
    产生的子进程)会处理多个请求,所以即使用 exit
    结束了脚本,进程也不会退出,pcntl_wait
    和 pcntl_waitpid
    自然也没用了,只能用 posix_kill(posix_getpid(), SIGTERM);
    结束掉当前进程(相当与不带参数的 kill
    命令,kill
    命令默认发送 SIGTERM 信号)。
    虽然子进程结束后,pcntl_wait
    和 pcntl_waitpid
    都能正常工作,但这两个函数实际上阻塞了父进程,没有起到异步的作用。如果不用这两个函数,当子进程结束时,由于父进程没有回收子进程,导致子进程成为僵尸(defunct)进程,导致系统资源被长时间占用。而且这些进程也不能被 kill
    命令结束,只有重启 php-fpm,也就是结束父进程。
    要想使父进程不等待子进程,可以通过 pcntl_signal(SIGCLD, SIG_IGN);
    或 pcntl_signal(SIGCHLD, SIG_IGN);
    (SIGCLD 和 SIGCHLD 都是子进程状态变更的信号,在大部分系统中作用相同;SIG_IGN 忽略该信号)告诉系统:父进程不关心子进程的结束,当子进程结束时,会由 init 进程来回收。
    提示:pcntl_signal
    是针对父进程设置的,所以在重现这个 bug 时记得重启 php-fpm。

  • 相关阅读:
    两台虚拟机(不同电脑)通信
    虚拟机使用网络助手
    strcpy_s
    线程创建几种方法
    JavaWeb——EL详解
    JavaWeb_EL语法
    JavaWeb_JavaBean
    JavaWeb_HttpSession之验证码
    JavaWeb_HttpSession之表单的重复提交
    JavaWeb——相对路径和绝对路径
  • 原文地址:https://www.cnblogs.com/liliuguang/p/13607386.html
Copyright © 2020-2023  润新知