宏文二手电脑回收中心 發表於 2019-6-11 14:14:00

php中如何实现多进程

<h1 style="text-align: center">php中如何实现多进程</h1>
<h2>一、总结</h2>
<h3>一句话总结:</h3>
<h5>php多进程需要pcntl,posix扩展支持</h5>
<p>可以通过 php - m 查看,没安装的话需要重新编译php,加上参数--enable-pcntl,posix一般默认会有</p>
<p>&nbsp;</p>
<h3>1、php多进程使用场景?</h3>
<h5>日常任务中,有时需要通过php脚本执行一些日志分析,队列处理等任务,当数据量比较大时,可以使用多进程来处理</h5>
<p>&nbsp;</p>
<h3>2、php的cli模式是什么?</h3>
<h5>命令行模式:cli : Command Line Interface(命令行接口)</h5>
<p>&nbsp;</p>
<h3>3、php多进程使用限制?</h3>
<h5>多进程实现只能在cli模式下,在web服务器环境下,会出现无法预期的结果,我测试报错:Call to undefined function: pcntl_fork()</h5>
<p>&nbsp;</p>
<h3>4、php多进程核心函数?</h3>
<h5>pcntl_fork(创建子进程)、pcntl_wait(阻塞当前进程)</h5>
<div class="cnblogs_code">
<pre>pcntl_fork:<span style="color: rgba(0, 0, 0, 1)">
一次调用两次返回,在父进程中返回子进程pid,在子进程中返回0,出错返回</span>-1<span style="color: rgba(0, 0, 0, 1)">。

pcntl_wait ( int </span>&amp;<span style="color: rgba(128, 0, 128, 1)">$status</span> [, int <span style="color: rgba(128, 0, 128, 1)">$options</span><span style="color: rgba(0, 0, 0, 1)"> ] ):
阻塞当前进程,直到任意一个子进程退出或收到一个结束当前进程的信号</span>,注意是结束当前进程的信号,子进程结束发送的SIGCHLD不算。使用<span style="color: rgba(128, 0, 128, 1)">$status返回子进程的状态码</span><span style="color: rgba(0, 0, 0, 1)">,并可以指定第二个参数来说明是否以阻塞状态调用
阻塞方式调用的,函数返回值为子进程的pid</span>,如果没有子进程返回值为-1<span style="color: rgba(0, 0, 0, 1)">;
非阻塞方式调用,函数还可以在有子进程在运行但没有结束的子进程时返回0。

pcntl_waitpid ( int </span><span style="color: rgba(128, 0, 128, 1)">$pid</span> , int &amp;<span style="color: rgba(128, 0, 128, 1)">$status</span> [, int <span style="color: rgba(128, 0, 128, 1)">$options</span><span style="color: rgba(0, 0, 0, 1)"> ] )
功能同pcntl_wait,区别为waitpid为等待指定pid的子进程。当pid为</span>-1时pcntl_waitpid与pcntl_wait 一样。在pcntl_wait和pcntl_waitpid两个函数中的<span style="color: rgba(128, 0, 128, 1)">$status中存了子进程的状态信息</span>。</pre>
</div>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<h3>5、php中一个始终保持固定个数的子进程在跑的例子?</h3>
<h5>根据需求使用pcntl_fork(创建子进程)、pcntl_wait(阻塞当前进程)等核心函数</h5>
<div class="cnblogs_code">
<pre>&lt;?<span style="color: rgba(0, 0, 0, 1)">php

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">最大的子进程数量</span>
<span style="color: rgba(128, 0, 128, 1)">$maxChildPro</span> = 8<span style="color: rgba(0, 0, 0, 1)">;

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">当前的子进程数量</span>
<span style="color: rgba(128, 0, 128, 1)">$curChildPro</span> = 0<span style="color: rgba(0, 0, 0, 1)">;

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">当子进程退出时,会触发该函数,当前子进程数-1</span>
<span style="color: rgba(0, 0, 255, 1)">function</span> sig_handler(<span style="color: rgba(128, 0, 128, 1)">$sig</span><span style="color: rgba(0, 0, 0, 1)">)
{
    </span><span style="color: rgba(0, 0, 255, 1)">global</span> <span style="color: rgba(128, 0, 128, 1)">$curChildPro</span><span style="color: rgba(0, 0, 0, 1)">;
    </span><span style="color: rgba(0, 0, 255, 1)">switch</span> (<span style="color: rgba(128, 0, 128, 1)">$sig</span><span style="color: rgba(0, 0, 0, 1)">) {
      </span><span style="color: rgba(0, 0, 255, 1)">case</span> SIGCHLD:
            <span style="color: rgba(0, 0, 255, 1)">echo</span> 'SIGCHLD', <span style="color: rgba(255, 0, 255, 1)">PHP_EOL</span><span style="color: rgba(0, 0, 0, 1)">;
            </span><span style="color: rgba(128, 0, 128, 1)">$curChildPro</span>--<span style="color: rgba(0, 0, 0, 1)">;
            </span><span style="color: rgba(0, 0, 255, 1)">break</span><span style="color: rgba(0, 0, 0, 1)">;
    }
}

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">配合pcntl_signal使用,简单的说,是为了让系统产生时间云,让信号捕捉函数能够捕捉到信号量</span>
<span style="color: rgba(0, 0, 255, 1)">declare</span>(ticks = 1<span style="color: rgba(0, 0, 0, 1)">);

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">注册子进程退出时调用的函数。SIGCHLD:在一个进程终止或者停止时,将SIGCHLD信号发送给其父进程。</span>
pcntl_signal(SIGCHLD, "sig_handler"<span style="color: rgba(0, 0, 0, 1)">);

</span><span style="color: rgba(0, 0, 255, 1)">while</span> (<span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">) {
    </span><span style="color: rgba(128, 0, 128, 1)">$curChildPro</span>++<span style="color: rgba(0, 0, 0, 1)">;
    </span><span style="color: rgba(128, 0, 128, 1)">$pid</span> =<span style="color: rgba(0, 0, 0, 1)"> pcntl_fork();
    </span><span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(128, 0, 128, 1)">$pid</span><span style="color: rgba(0, 0, 0, 1)">) {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">父进程运行代码,达到上限时父进程阻塞等待任一子进程退出后while循环继续</span>
      <span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(128, 0, 128, 1)">$curChildPro</span> &gt;= <span style="color: rgba(128, 0, 128, 1)">$maxChildPro</span><span style="color: rgba(0, 0, 0, 1)">) {
            pcntl_wait(</span><span style="color: rgba(128, 0, 128, 1)">$status</span><span style="color: rgba(0, 0, 0, 1)">);
      }
    } </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">子进程运行代码</span>
      <span style="color: rgba(128, 0, 128, 1)">$s</span> = <span style="color: rgba(0, 128, 128, 1)">rand</span>(2, 6<span style="color: rgba(0, 0, 0, 1)">);
      </span><span style="color: rgba(0, 128, 128, 1)">sleep</span>(<span style="color: rgba(128, 0, 128, 1)">$s</span><span style="color: rgba(0, 0, 0, 1)">);
      </span><span style="color: rgba(0, 0, 255, 1)">echo</span> "child sleep <span style="color: rgba(128, 0, 128, 1)">$s</span> second quit", <span style="color: rgba(255, 0, 255, 1)">PHP_EOL</span><span style="color: rgba(0, 0, 0, 1)">;
      </span><span style="color: rgba(0, 0, 255, 1)">exit</span><span style="color: rgba(0, 0, 0, 1)">;
    }
}</span></pre>
</div>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<h2>二、php多进程总结</h2>
<p>参考:php多进程总结<br>https://www.cnblogs.com/leezhxing/p/5223289.html"&gt;php多进程总结</p>
<div class="clear">&nbsp;</div>
<div class="postBody">
<div id="cnblogs_post_body" class="blogpost-body">
<div><strong>本文部分来自网络参考,部分自己总结,由于一直保存在笔记中,并没有记录参考文章地址,如有侵权请通知删除。最近快被业务整疯了,这个等抽时间还需要好好的整理一番。</strong></div>
<div>&nbsp;</div>
<div><strong>多进程--fork</strong></div>
<div>
<p>场景:日常任务中,有时需要通过php脚本执行一些日志分析,队列处理等任务,当数据量比较大时,可以使用多进程来处理。</p>
<p>准备:php多进程需要pcntl,posix扩展支持,可以通过 php - m 查看,没安装的话需要重新编译php,加上参数<code>--enable-pcntl,posix一般默认会有。</code></p>
<p>注意:</p>
<p>   &nbsp;多进程实现只能在cli模式下,在web服务器环境下,会出现无法预期的结果,我测试<code>报错:<code>Call to undefined function: pcntl_fork()</code></code></p>
<p><code><code>&nbsp; &nbsp;</code></code>一个错误&nbsp;pcntl_fork causing “errno=32 Broken pipe” #474 ,看https://github.com/phpredis/phpredis/issues/474</p>




</div>
<p>&nbsp; &nbsp; &nbsp; &nbsp; <span style="background-color: rgba(255, 204, 153, 1)">注意两点:如果是在循环中创建子进程,那么子进程中最后要exit,防止子进程进入循环。</span><br><span style="background-color: rgba(255, 204, 153, 1)">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 子进程中的打开连接不能拷贝,使用的还是主进程的,需要用多例模式。</span></p>
<div>
<p>pcntl_fork:</p>
<p>  一次调用两次返回,在父进程中返回子进程pid,在子进程中返回0,出错返回-1。</p>
<p>&nbsp;pcntl_wait&nbsp;( int &amp;$status [, int $options ] ):</p>
<p>   &nbsp;阻塞当前进程,直到任意一个子进程退出或收到一个结束当前进程的信号,注意是结束当前进程的信号,子进程结束发送的SIGCHLD不算。使用$status返回子进程的状态码,并可以指定第二个参数来说明是否以阻塞状态调用</p>
<p>&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;阻塞方式调用的,函数返回值为子进程的pid,如果没有子进程返回值为-1;</p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; 非阻塞方式调用,函数还可以在有子进程在运行但没有结束的子进程时返回0。</p>





</div>
<div>pcntl_waitpid ( int $pid , int &amp;$status [, int $options ] )</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;功能同pcntl_wait,区别为waitpid为等待指定pid的子进程。当pid为-1时pcntl_waitpid与pcntl_wait 一样。在pcntl_wait和pcntl_waitpid两个函数中的$status中存了子进程的状态信息。</div>
<div>&nbsp;</div>
<div>检测是否是cli模式</div>
<div>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">* 确保这个函数只能运行在SHELL中 </span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 128, 128, 1)">substr</span>(<span style="color: rgba(0, 128, 128, 1)">php_sapi_name</span>(), 0, 3) !== 'cli'<span style="color: rgba(0, 0, 0, 1)">) {
  </span><span style="color: rgba(0, 0, 255, 1)">die</span>("cli mode only"<span style="color: rgba(0, 0, 0, 1)">);
}</span></pre>
</div>
<p>&nbsp;</p>
</div>
<div>SHELL脚本实现多进程(Qbus的多进程就是这样实现的,只不过用上了nohup 和 &amp; 改为守护进程):</div>
<div>
<div>
<div class="cnblogs_code">
<pre>#!/bin/<span style="color: rgba(0, 0, 0, 1)">bash

</span><span style="color: rgba(0, 0, 255, 1)">for</span>((i=<span style="color: rgba(128, 0, 128, 1)">1</span>;i&lt;=<span style="color: rgba(128, 0, 128, 1)">8</span>;i++<span style="color: rgba(0, 0, 0, 1)">))
</span><span style="color: rgba(0, 0, 255, 1)">do</span>   
    /usr/local/bin/php multiprocessTest.php &amp;<span style="color: rgba(0, 0, 0, 1)">
done

wait</span></pre>
</div>
</div>
<p>上面的shell程序,列了一个很简单的多进程程序,用一个for循环,实现了8进程并发来跑multiprocessTest.php这个程序。最后的wait语句,也可以使主进程,再等待所有进程都执行完后再往下执行的需求。</p>
<p>这个程序是没有问题的,很多现有的代码也都这样实现,但是这个程序的并发数是不可控的,即我们无法根据机器的核数去调度每一个进程的开关。</p>
<p>若我们的机器有8核或者更多,上面的程序是没有问题的,所有核都能充分利用,并且互相之间,没有争抢资源的情况出现。</p>
<p>但我们的机器要没有8核的话会是什么情况,同一时间运行的进程数多于核数,那么系统就会出现进程分配调度的问题,争抢资源也跟着相应而来,一个进程不能保证独立连续的执行,所有的进程运行会听从系统的调度,这样就会有更多的不确定因素出现。</p>
</div>
<div>一个始终保持固定个数的子进程在跑的例子</div>
<div>
<div class="cnblogs_code">
<pre>&lt;?<span style="color: rgba(0, 0, 0, 1)">php

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">最大的子进程数量</span>
<span style="color: rgba(128, 0, 128, 1)">$maxChildPro</span> = 8<span style="color: rgba(0, 0, 0, 1)">;

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">当前的子进程数量</span>
<span style="color: rgba(128, 0, 128, 1)">$curChildPro</span> = 0<span style="color: rgba(0, 0, 0, 1)">;

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">当子进程退出时,会触发该函数,当前子进程数-1</span>
<span style="color: rgba(0, 0, 255, 1)">function</span> sig_handler(<span style="color: rgba(128, 0, 128, 1)">$sig</span><span style="color: rgba(0, 0, 0, 1)">)
{
    </span><span style="color: rgba(0, 0, 255, 1)">global</span> <span style="color: rgba(128, 0, 128, 1)">$curChildPro</span><span style="color: rgba(0, 0, 0, 1)">;
    </span><span style="color: rgba(0, 0, 255, 1)">switch</span> (<span style="color: rgba(128, 0, 128, 1)">$sig</span><span style="color: rgba(0, 0, 0, 1)">) {
      </span><span style="color: rgba(0, 0, 255, 1)">case</span> SIGCHLD:
            <span style="color: rgba(0, 0, 255, 1)">echo</span> 'SIGCHLD', <span style="color: rgba(255, 0, 255, 1)">PHP_EOL</span><span style="color: rgba(0, 0, 0, 1)">;
            </span><span style="color: rgba(128, 0, 128, 1)">$curChildPro</span>--<span style="color: rgba(0, 0, 0, 1)">;
            </span><span style="color: rgba(0, 0, 255, 1)">break</span><span style="color: rgba(0, 0, 0, 1)">;
    }
}

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">配合pcntl_signal使用,简单的说,是为了让系统产生时间云,让信号捕捉函数能够捕捉到信号量</span>
<span style="color: rgba(0, 0, 255, 1)">declare</span>(ticks = 1<span style="color: rgba(0, 0, 0, 1)">);

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">注册子进程退出时调用的函数。SIGCHLD:在一个进程终止或者停止时,将SIGCHLD信号发送给其父进程。</span>
pcntl_signal(SIGCHLD, "sig_handler"<span style="color: rgba(0, 0, 0, 1)">);

</span><span style="color: rgba(0, 0, 255, 1)">while</span> (<span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">) {
    </span><span style="color: rgba(128, 0, 128, 1)">$curChildPro</span>++<span style="color: rgba(0, 0, 0, 1)">;
    </span><span style="color: rgba(128, 0, 128, 1)">$pid</span> =<span style="color: rgba(0, 0, 0, 1)"> pcntl_fork();
    </span><span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(128, 0, 128, 1)">$pid</span><span style="color: rgba(0, 0, 0, 1)">) {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">父进程运行代码,达到上限时父进程阻塞等待任一子进程退出后while循环继续</span>
      <span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(128, 0, 128, 1)">$curChildPro</span> &gt;= <span style="color: rgba(128, 0, 128, 1)">$maxChildPro</span><span style="color: rgba(0, 0, 0, 1)">) {
            pcntl_wait(</span><span style="color: rgba(128, 0, 128, 1)">$status</span><span style="color: rgba(0, 0, 0, 1)">);
      }
    } </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">子进程运行代码</span>
      <span style="color: rgba(128, 0, 128, 1)">$s</span> = <span style="color: rgba(0, 128, 128, 1)">rand</span>(2, 6<span style="color: rgba(0, 0, 0, 1)">);
      </span><span style="color: rgba(0, 128, 128, 1)">sleep</span>(<span style="color: rgba(128, 0, 128, 1)">$s</span><span style="color: rgba(0, 0, 0, 1)">);
      </span><span style="color: rgba(0, 0, 255, 1)">echo</span> "child sleep <span style="color: rgba(128, 0, 128, 1)">$s</span> second quit", <span style="color: rgba(255, 0, 255, 1)">PHP_EOL</span><span style="color: rgba(0, 0, 0, 1)">;
      </span><span style="color: rgba(0, 0, 255, 1)">exit</span><span style="color: rgba(0, 0, 0, 1)">;
    }
}</span></pre>
</div>
</div>
<div>
<p>&nbsp;</p>
</div>
<div>一个使用waitpid函数等待全部子进程退出,防止僵尸进程的例子</div>
<div>
<div class="cnblogs_code">
<pre>&lt;?<span style="color: rgba(0, 0, 0, 1)">php

</span><span style="color: rgba(128, 0, 128, 1)">$childs</span> = <span style="color: rgba(0, 0, 255, 1)">array</span><span style="color: rgba(0, 0, 0, 1)">();

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Fork10个子进程</span>
<span style="color: rgba(0, 0, 255, 1)">for</span> (<span style="color: rgba(128, 0, 128, 1)">$i</span> = 0; <span style="color: rgba(128, 0, 128, 1)">$i</span> &lt; 10; <span style="color: rgba(128, 0, 128, 1)">$i</span>++<span style="color: rgba(0, 0, 0, 1)">) {
    </span><span style="color: rgba(128, 0, 128, 1)">$pid</span> =<span style="color: rgba(0, 0, 0, 1)"> pcntl_fork();
    </span><span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(128, 0, 128, 1)">$pid</span> == -1<span style="color: rgba(0, 0, 0, 1)">)
      </span><span style="color: rgba(0, 0, 255, 1)">die</span>('Could not fork'<span style="color: rgba(0, 0, 0, 1)">);

    </span><span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(128, 0, 128, 1)">$pid</span><span style="color: rgba(0, 0, 0, 1)">) {
      </span><span style="color: rgba(0, 0, 255, 1)">echo</span> "parent \n"<span style="color: rgba(0, 0, 0, 1)">;
      </span><span style="color: rgba(128, 0, 128, 1)">$childs</span>[] = <span style="color: rgba(128, 0, 128, 1)">$pid</span><span style="color: rgba(0, 0, 0, 1)">;
    } </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Sleep $i+1 (s). 子进程可以得到$i参数</span>
      <span style="color: rgba(0, 128, 128, 1)">sleep</span>(<span style="color: rgba(128, 0, 128, 1)">$i</span> + 1<span style="color: rgba(0, 0, 0, 1)">);

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 子进程需要exit,防止子进程也进入for循环</span>
      <span style="color: rgba(0, 0, 255, 1)">exit</span><span style="color: rgba(0, 0, 0, 1)">();
    }
}

</span><span style="color: rgba(0, 0, 255, 1)">while</span> (<span style="color: rgba(0, 128, 128, 1)">count</span>(<span style="color: rgba(128, 0, 128, 1)">$childs</span>) &gt; 0<span style="color: rgba(0, 0, 0, 1)">) {
    </span><span style="color: rgba(0, 0, 255, 1)">foreach</span> (<span style="color: rgba(128, 0, 128, 1)">$childs</span> <span style="color: rgba(0, 0, 255, 1)">as</span> <span style="color: rgba(128, 0, 128, 1)">$key</span> =&gt; <span style="color: rgba(128, 0, 128, 1)">$pid</span><span style="color: rgba(0, 0, 0, 1)">) {
      </span><span style="color: rgba(128, 0, 128, 1)">$res</span> = pcntl_waitpid(<span style="color: rgba(128, 0, 128, 1)">$pid</span>, <span style="color: rgba(128, 0, 128, 1)">$status</span>,<span style="color: rgba(0, 0, 0, 1)"> WNOHANG);

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">-1代表error, 大于0代表子进程已退出,返回的是子进程的pid,非阻塞时0代表没取到退出子进程</span>
      <span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(128, 0, 128, 1)">$res</span> == -1 || <span style="color: rgba(128, 0, 128, 1)">$res</span> &gt; 0<span style="color: rgba(0, 0, 0, 1)">)
            </span><span style="color: rgba(0, 0, 255, 1)">unset</span>(<span style="color: rgba(128, 0, 128, 1)">$childs</span>[<span style="color: rgba(128, 0, 128, 1)">$key</span><span style="color: rgba(0, 0, 0, 1)">]);
    }

    </span><span style="color: rgba(0, 128, 128, 1)">sleep</span>(1<span style="color: rgba(0, 0, 0, 1)">);
}</span></pre>
</div>
<p>&nbsp;</p>
</div>
<div>一个实际的例子,php实现并发log拷贝</div>
<div>
<div class="cnblogs_code">
<pre>&lt;?<span style="color: rgba(0, 0, 0, 1)">php
</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> _fetchLog()
{
    </span><span style="color: rgba(128, 0, 128, 1)">$password</span>      = <span style="color: rgba(128, 0, 128, 1)">$this</span>-&gt;<span style="color: rgba(0, 0, 0, 1)">_getPassword();
    </span><span style="color: rgba(128, 0, 128, 1)">$online_log_path</span> = NginxConf::getArchiveDir(<span style="color: rgba(128, 0, 128, 1)">$this</span>-&gt;<span style="color: rgba(0, 0, 0, 1)">_stat_day);
    </span><span style="color: rgba(128, 0, 128, 1)">$task_log_path</span>   = QFrameConfig::getConfig('LOG_PATH'<span style="color: rgba(0, 0, 0, 1)">);
    </span><span style="color: rgba(128, 0, 128, 1)">$children</span>      = <span style="color: rgba(0, 0, 255, 1)">array</span><span style="color: rgba(0, 0, 0, 1)">();
    </span><span style="color: rgba(128, 0, 128, 1)">$success</span>         = <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;
    </span><span style="color: rgba(0, 0, 255, 1)">foreach</span>(<span style="color: rgba(128, 0, 128, 1)">$this</span>-&gt;_server_list <span style="color: rgba(0, 0, 255, 1)">as</span> <span style="color: rgba(128, 0, 128, 1)">$host</span> =&gt; <span style="color: rgba(128, 0, 128, 1)">$value</span><span style="color: rgba(0, 0, 0, 1)">)
    {
      </span><span style="color: rgba(128, 0, 128, 1)">$local_dir</span> = <span style="color: rgba(128, 0, 128, 1)">$this</span>-&gt;_prepareLocalDir(<span style="color: rgba(128, 0, 128, 1)">$host</span><span style="color: rgba(0, 0, 0, 1)">);
      </span><span style="color: rgba(128, 0, 128, 1)">$task_log</span>= "<span style="color: rgba(128, 0, 128, 1)">$task_log_path</span>/fetch_log.<span style="color: rgba(128, 0, 128, 1)">$host</span>"<span style="color: rgba(0, 0, 0, 1)">;
      </span><span style="color: rgba(128, 0, 128, 1)">$cmd</span> = "sshpass -p <span style="color: rgba(128, 0, 128, 1)">$password</span> rsync -av -e 'ssh -o StrictHostKeyChecking=no' <span style="color: rgba(128, 0, 128, 1)">$host</span>:<span style="color: rgba(128, 0, 128, 1)">$online_log_path</span>/* <span style="color: rgba(128, 0, 128, 1)">$local_dir</span> &gt;&gt; <span style="color: rgba(128, 0, 128, 1)">$task_log</span> 2&gt;&amp;1"<span style="color: rgba(0, 0, 0, 1)">;
      </span><span style="color: rgba(128, 0, 128, 1)">$pid</span> =<span style="color: rgba(0, 0, 0, 1)"> pcntl_fork();
      </span><span style="color: rgba(0, 0, 255, 1)">if</span>(-1 === <span style="color: rgba(128, 0, 128, 1)">$pid</span><span style="color: rgba(0, 0, 0, 1)">)
      {
            LogSvc</span>::<span style="color: rgba(0, 128, 128, 1)">log</span>('stat_pv_by_citycode_error', 'could not fork'<span style="color: rgba(0, 0, 0, 1)">);
            </span><span style="color: rgba(0, 0, 255, 1)">exit</span>('could not fork'<span style="color: rgba(0, 0, 0, 1)">);
      }
      </span><span style="color: rgba(0, 0, 255, 1)">else</span> <span style="color: rgba(0, 0, 255, 1)">if</span>(0 === <span style="color: rgba(128, 0, 128, 1)">$pid</span><span style="color: rgba(0, 0, 0, 1)">)
      {
            </span><span style="color: rgba(0, 128, 128, 1)">system</span>(<span style="color: rgba(128, 0, 128, 1)">$cmd</span>, <span style="color: rgba(128, 0, 128, 1)">$return_value</span><span style="color: rgba(0, 0, 0, 1)">);
            </span><span style="color: rgba(0, 0, 255, 1)">if</span>(0 !== <span style="color: rgba(128, 0, 128, 1)">$return_value</span><span style="color: rgba(0, 0, 0, 1)">)
            {
                LogSvc</span>::<span style="color: rgba(0, 128, 128, 1)">log</span>('stat_pv_by_citycode_error', "rsync <span style="color: rgba(128, 0, 128, 1)">$host</span> error"<span style="color: rgba(0, 0, 0, 1)">);
            }
            </span><span style="color: rgba(0, 0, 255, 1)">exit</span>(<span style="color: rgba(128, 0, 128, 1)">$return_value</span><span style="color: rgba(0, 0, 0, 1)">);
      }
      </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)">
      {
            </span><span style="color: rgba(128, 0, 128, 1)">$children</span>[<span style="color: rgba(128, 0, 128, 1)">$pid</span>] = 1<span style="color: rgba(0, 0, 0, 1)">;
      }
    }
    </span><span style="color: rgba(0, 0, 255, 1)">while</span>(!<span style="color: rgba(0, 0, 255, 1)">empty</span>(<span style="color: rgba(128, 0, 128, 1)">$children</span><span style="color: rgba(0, 0, 0, 1)">))
    {
      </span><span style="color: rgba(128, 0, 128, 1)">$pid</span> = pcntl_waitpid(-1, <span style="color: rgba(128, 0, 128, 1)">$status</span>,<span style="color: rgba(0, 0, 0, 1)"> WNOHANG);
      </span><span style="color: rgba(0, 0, 255, 1)">if</span>(0 === <span style="color: rgba(128, 0, 128, 1)">$pid</span><span style="color: rgba(0, 0, 0, 1)">)
      {
            </span><span style="color: rgba(0, 128, 128, 1)">sleep</span>(1<span style="color: rgba(0, 0, 0, 1)">);
      }
      </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)">
      {
            </span><span style="color: rgba(0, 0, 255, 1)">if</span>(0 !== pcntl_wexitstatus(<span style="color: rgba(128, 0, 128, 1)">$status</span><span style="color: rgba(0, 0, 0, 1)">))
            {
                </span><span style="color: rgba(128, 0, 128, 1)">$success</span> = <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">;
            }
            </span><span style="color: rgba(0, 0, 255, 1)">unset</span>(<span style="color: rgba(128, 0, 128, 1)">$children</span>[<span style="color: rgba(128, 0, 128, 1)">$pid</span><span style="color: rgba(0, 0, 0, 1)">]);
      }
    }
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(128, 0, 128, 1)">$success</span><span style="color: rgba(0, 0, 0, 1)">;
}</span>&nbsp;</pre>
</div>
</div>
<div><hr></div>
<div>&nbsp;</div>
<div><span style="font-size: medium"><strong>多进程--信号</strong></span></div>
<div>&nbsp;</div>
<div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">同类信号只能存储一个,多余的自动丢弃</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">可以发送小于0的值代表发给全部子进程,包括自己,其实是一个组的进程。</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp;</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">PCNTL使用ticks来作为信号处理机制(signal handle callback mechanism),可以最小程度地降低处理异步事件时的负载。何谓ticks?Tick 是一个在代码段中解释器每执行 N 条低级语句就会发生的事件,这个代码段需要通过declare来指定。<br></span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp;</span></div>
<div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">    pcntl_signal ( int $signo , callback $handler [, bool $restart_syscalls ] ) //为某个SIG注册一个处理函数</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp; &nbsp; &nbsp;<code>posix_kill(posix_getpid(),&nbsp;SIGHUP);&nbsp;<code>为自己生成SIGHUP信号</code></code></span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium"><code><code>&nbsp; &nbsp; &nbsp; &nbsp;declare(ticks&nbsp;=&nbsp;1); //php &lt; 5.3</code></code></span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp; &nbsp; &nbsp;<strong>pcntl_signal_dispatch</strong>&nbsp;(&nbsp;void&nbsp;) &nbsp;</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp; &nbsp; &nbsp; &nbsp; 调用每个等待信号通过pcntl_signal()&nbsp;安装的处理器。说明一下:<code>pcntl_signal()</code>函数仅仅是注册信号和它的处理方法,真正接收到信号并调用其处理方法的是<code>pcntl_signal_dispatch()</code>函数</span><span style="font-family: 方正兰亭黑; font-size: medium; line-height: 1.5">必须在循环里调用,为了检测是否有新的信号等待dispatching。</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp; &nbsp; &nbsp;<code>pcntl_signal_dispatch()</code></span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">      这个函数是PHP 5.3以上才支持的,如果你的PHP版本大于5.3,建议使用这个方法调用信号处理器。5.3以下的版本需要在注册信号之前加一句:<code>declare(ticks = 1);</code>表示每执行一条低级指令,就检查一次信号,如果检测到注册的信号,就调用其信号处理器。</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp; &nbsp; &nbsp;pcntl_alarm ( int $seconds )</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp; &nbsp; &nbsp; &nbsp; 设置一个$seconds秒后发送SIGALRM信号的计数器</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">5.发送信号:</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp;</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp; &nbsp; posix_kill(): 向进程发送信号。&nbsp;</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp; &nbsp; SIGINT : 通过键盘CTRL+C.</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp; &nbsp; SIGTERM :&nbsp;有时候进程失去响应了还会执行<code>kill </code>命令,未加任何其他参数的话,程序会接收到一个SIGTERM信号。 &nbsp; &nbsp; &nbsp; &nbsp;</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;程序收到上面两个信号的时候,默认都会结束执行,可以通过注册信号改变默认行为。</span></div>
<div>&nbsp;</div>





</div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">一个注册信号处理器的例子,得到结论:</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">sleep函数会被信号唤醒,不再休眠,返回唤醒时剩余的秒数</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">对于说法同类信号只能存储一个,多余的自动丢弃,测试发现多次CTRL+C后,进程会先唤醒sleep,再唤醒usleep,当执行到pcntl_signal_dispatch时,会一次输出多个“SIGINT”,不知道存储一个是不是只的是什么地方。</span></div>
<div>
<div class="cnblogs_code">
<pre>&lt;?<span style="color: rgba(0, 0, 0, 1)">php

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 定义一个处理器,接收到SIGINT信号后只输出一行信息</span>
<span style="color: rgba(0, 0, 255, 1)">function</span> signalHandler(<span style="color: rgba(128, 0, 128, 1)">$signal</span><span style="color: rgba(0, 0, 0, 1)">)
{
    </span><span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(128, 0, 128, 1)">$signal</span> ==<span style="color: rgba(0, 0, 0, 1)"> SIGINT) {
      </span><span style="color: rgba(0, 0, 255, 1)">echo</span> 'SIGINT', <span style="color: rgba(255, 0, 255, 1)">PHP_EOL</span><span style="color: rgba(0, 0, 0, 1)">;
    }
}

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 信号注册:当接收到SIGINT信号时,调用signalHandler()函数</span>
pcntl_signal(SIGINT, 'signalHandler'<span style="color: rgba(0, 0, 0, 1)">);

</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">*
* PHP &lt; 5.3 使用
* 配合pcntl_signal使用,表示每执行一条低级指令,就检查一次信号,如果检测到注册的信号,就调用其信号处理器。
</span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">if</span> (!<span style="color: rgba(0, 128, 128, 1)">function_exists</span>("pcntl_signal_dispatch"<span style="color: rgba(0, 0, 0, 1)">)) {
    </span><span style="color: rgba(0, 0, 255, 1)">declare</span>(ticks=1<span style="color: rgba(0, 0, 0, 1)">);
}

</span><span style="color: rgba(0, 0, 255, 1)">while</span> (<span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">) {
    </span><span style="color: rgba(128, 0, 128, 1)">$s</span> = <span style="color: rgba(0, 128, 128, 1)">sleep</span>(10<span style="color: rgba(0, 0, 0, 1)">);
    </span><span style="color: rgba(0, 0, 255, 1)">echo</span> <span style="color: rgba(128, 0, 128, 1)">$s</span>, <span style="color: rgba(255, 0, 255, 1)">PHP_EOL</span>; <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">信号会唤醒sleep,返回剩余的秒数。

// do something</span>
    <span style="color: rgba(0, 0, 255, 1)">for</span> (<span style="color: rgba(128, 0, 128, 1)">$i</span> = 0; <span style="color: rgba(128, 0, 128, 1)">$i</span> &lt; 5; <span style="color: rgba(128, 0, 128, 1)">$i</span>++<span style="color: rgba(0, 0, 0, 1)">) {
      </span><span style="color: rgba(0, 0, 255, 1)">echo</span> <span style="color: rgba(128, 0, 128, 1)">$i</span> . <span style="color: rgba(255, 0, 255, 1)">PHP_EOL</span><span style="color: rgba(0, 0, 0, 1)">;
      </span><span style="color: rgba(0, 128, 128, 1)">usleep</span>(100000<span style="color: rgba(0, 0, 0, 1)">);
    }

    </span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">*
   * PHP &gt;= 5.3
   * 调用已安装的信号处理器
   * 必须在循环里调用,为了检测是否有新的信号等待dispatching。
   </span><span style="color: rgba(0, 128, 0, 1)">*/</span>
    <span style="color: rgba(0, 0, 255, 1)">if</span> (!<span style="color: rgba(0, 128, 128, 1)">function_exists</span>("pcntl_signal_dispatch"<span style="color: rgba(0, 0, 0, 1)">)) {
      pcntl_signal_dispatch();
    }

}</span></pre>
</div>
<p>&nbsp;</p>
</div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">一个隔5s发一个信号的例子,通过pcntl_alarm实现</span></div>
<div>
<div class="cnblogs_code">
<pre>&lt;?<span style="color: rgba(0, 0, 0, 1)">php

</span><span style="color: rgba(0, 0, 255, 1)">declare</span>(ticks = 1<span style="color: rgba(0, 0, 0, 1)">);

</span><span style="color: rgba(0, 0, 255, 1)">function</span> signal_handler(<span style="color: rgba(128, 0, 128, 1)">$signal</span><span style="color: rgba(0, 0, 0, 1)">) {
    </span><span style="color: rgba(0, 0, 255, 1)">print</span> "Caught SIGALRM\n"<span style="color: rgba(0, 0, 0, 1)">;
    pcntl_alarm(</span>5<span style="color: rgba(0, 0, 0, 1)">);
}

pcntl_signal(SIGALRM</span>, "signal_handler", <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">);
pcntl_alarm(</span>5<span style="color: rgba(0, 0, 0, 1)">);

</span><span style="color: rgba(0, 0, 255, 1)">for</span><span style="color: rgba(0, 0, 0, 1)">(;;) {
}</span></pre>
</div>
<p>&nbsp;</p>
</div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">一个通过发送信号杀死进程的例子,信号可以发给自己也可以发给其他进程。</span></div>
<div>
<div class="cnblogs_code">
<pre>&lt;?<span style="color: rgba(0, 0, 0, 1)">php

</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">*
* 父进程通过pcntl_wait等待子进程退出
* 子进程通过信号kill自己,也可以在父进程中发送kil信号结束子进程
</span><span style="color: rgba(0, 128, 0, 1)">*/</span>

<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">生成子进程</span>
<span style="color: rgba(128, 0, 128, 1)">$pid</span> =<span style="color: rgba(0, 0, 0, 1)"> pcntl_fork();
</span><span style="color: rgba(0, 0, 255, 1)">if</span>(<span style="color: rgba(128, 0, 128, 1)">$pid</span> == -1<span style="color: rgba(0, 0, 0, 1)">){
    </span><span style="color: rgba(0, 0, 255, 1)">die</span>('could not fork'<span style="color: rgba(0, 0, 0, 1)">);
}</span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)">{
    </span><span style="color: rgba(0, 0, 255, 1)">if</span>(<span style="color: rgba(128, 0, 128, 1)">$pid</span><span style="color: rgba(0, 0, 0, 1)">){
      </span><span style="color: rgba(128, 0, 128, 1)">$status</span> = 0<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">阻塞父进程,直到子进程结束,不适合需要长时间运行的脚本.
      //可使用pcntl_wait($status, WNOHANG)实现非阻塞式</span>
      pcntl_wait(<span style="color: rgba(128, 0, 128, 1)">$status</span><span style="color: rgba(0, 0, 0, 1)">);
      </span><span style="color: rgba(0, 0, 255, 1)">exit</span><span style="color: rgba(0, 0, 0, 1)">;
    }</span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)">{
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">结束当前子进程,以防止生成僵尸进程</span>
      <span style="color: rgba(0, 0, 255, 1)">if</span>(<span style="color: rgba(0, 128, 128, 1)">function_exists</span>("posix_kill"<span style="color: rgba(0, 0, 0, 1)">)){
            posix_kill(</span><span style="color: rgba(0, 128, 128, 1)">getmypid</span>(),<span style="color: rgba(0, 0, 0, 1)"> SIGTERM);
      }</span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)">{
            </span><span style="color: rgba(0, 128, 128, 1)">system</span>('kill -9'. <span style="color: rgba(0, 128, 128, 1)">getmypid</span><span style="color: rgba(0, 0, 0, 1)">());
      }
      </span><span style="color: rgba(0, 0, 255, 1)">exit</span><span style="color: rgba(0, 0, 0, 1)">;
    }
}</span></pre>
</div>
</div>
<hr></div>
<div>&nbsp;</div>
<div><span style="font-size: medium"><strong>多进程--僵尸进程</strong></span></div>
<div><span style="font-size: medium"><strong>&nbsp;</strong></span></div>
<div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">当子进程比父进程先退出,但是父进程还在运行中并且可能很长一段时间不会退出,子进程就变成了僵尸进程。然后内核会找到这个僵尸进程的PPID,给这个PPID发送一个SIGCHLD信号。如果父进程中没有通过pcntl_wait或者pcntl_waitpid函数处理,并且没有通过posix_kill发送退出信号给子进程,那么这个子进程就彻底僵尸了。</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ps aux查看到时Z(zombie)状态。</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 一般需要在父进程结束前回收子进程先,pcntl_wait()函数会将父进程挂起,直到一个子进程退出。</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 如果父进程先挂了,子进程会被1号进程接管,当子进程结束时1号进程会自动回收。所以关闭僵尸进程的另一种方法就是关闭他们的父进程。</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 子进程如何得知父进程退出:</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;1. 当父进程退出时,会有一个INIT进程来领养这个子进程。这个INIT进程的进程号为1,所以子进程可以通过使用getppid()来取得当前父进程的pid。如果返回的是1,表明父进程已经变为INIT进程,则原进程已经推出。</span>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp;</span></div>
<span style="font-family: 方正兰亭黑; font-size: medium">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 2. 使用kill函数,php中是posix_kill,向原有的父进程发送空信号(kill(pid, 0))。使用这个方法对某个进程的存在性进行检查,而不会真的发送信号。所以,如果这个函数返回-1表示父进程已经退出。</span></div>
<div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp;</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 僵尸进程:</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;<span style="font-family: 微软雅黑; font-size: medium">在UNIX 系统中,一个进程结束了,但是他的父进程没有等待(调用wait / waitpid)他,那么他将变成一个僵尸进程。</span></span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium"><span style="font-family: 微软雅黑; font-size: medium">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;<span style="font-family: 微软雅黑; font-size: medium">僵尸进程是一个早已死亡的进程,但在进程表 (processs table)中仍占了一个位置(slot)。</span></span></span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium"><span style="font-family: 微软雅黑; font-size: medium"><span style="font-family: 微软雅黑; font-size: medium">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;僵尸进程不及时回收,会在系统中占用一个进程表项,如果这种僵尸进程过多,最后系统就没有可以用的进程表项,于是也无法再运行其它的程序。</span></span></span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 任何进程在退出前(exit退出)都会变为僵尸进程。用于保存进程的状态等信息。为什么呢?</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;<span style="font-family: 微软雅黑; font-size: medium">子进程的结束和父进程的运行是一个异步过程,即父进程永远无法预测子进程到底什么时候结束。那么 会不会因为父进程太忙来不及 wait 子进程,或者说不知道子进程什么时候结束,而丢失子进程结束时的状态信息呢?不会。因为UNIX提供了一种机制可以保证,只要父进程想知道子进程结束时的 状态信息,就可以得到。这种机制就是:当子进程走完了自己的生命周期后,它会执行exit()系统调用,内核释放该进程所有的资源,包括打开的文件,占用 的内存等。但是仍然为其保留一定的信息(包括进程号the process ID,退出码exit code,退出状态the terminationstatus of the process,运行时间the amount of CPU time taken by the process等),这些数据会一直保留到系统将它传递给它的父进程为止,直到父进程通过wait / waitpid来取时才释放。</span></span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 预防僵尸进程</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium"><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</span></span>
<p><span style="font-family: 微软雅黑; font-size: medium">(1) 父进程通过wait和waitpid等函数等待子进程结束,这会导致父进程挂起。它不适合子进程需要长时间运行的情况(会导致超时)。&nbsp;&nbsp; &nbsp; &nbsp;</span></p>
<p><span style="font-family: 微软雅黑; font-size: medium">&nbsp;&nbsp;&nbsp; 执行wait()或waitpid()系统调用,则子进程在终止后会立即把它在进程表中的数据返回给父进程,此时系统会立即删除该进入点。在这种情形下就不会产生defunct进程。</span></p>
<p><span style="font-family: 微软雅黑; font-size: medium">&nbsp;&nbsp;&nbsp; (2) 如果父进程很忙,那么可以用signal函数为SIGCHLD安装handler。在子进程结束后,父进程会收到该信号,可以在handler中调用wait回收。</span></p>
<p><span style="font-family: 微软雅黑; font-size: medium">&nbsp;&nbsp;&nbsp; (3) 如果父进程不关心子进程什么时候结束,那么可以用signal(SIGCLD, SIG_IGN)或signal(SIGCHLD, SIG_IGN)通知内核,自己对子进程的结束不感兴趣,那么子进程结束后,内核会回收,并不再给父进程发送信号</span></p>
<p><span style="font-family: 微软雅黑; font-size: medium">&nbsp;&nbsp;&nbsp; (4)fork两次,父进程fork一个子进程,然后继续工作,子进程fork一个孙进程后退出,那么孙进程被init接管,孙进程结束后,init会回收。不过子进程的回收还要自己做。</span></p>
</div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 查看和清零僵尸进程:测试发现通过 ps -ef 和 aux 是不能看到僵尸进程的。</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 需要通过top命令实时看到当前系统的僵尸进程个数。</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</span><img alt="" data-media-type="image" data-attr-org-src-id="A6126D60D955486EBB3A5DADA9513C07" data-attr-org-img-file="file:///C:/Users/lizhe/AppData/Local/YNote/data/sina2166797151/111faca45e8240b7baeea9761b1d77dc/clipboard.png"></div>
<div>&nbsp;</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 用ps命令查看僵尸进程:</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;
<pre>            ps -A -ostat,ppid,pid,cmd | grep -e '^'</pre>
<pre></pre>
<pre>         命令注解:<br>
  -A 参数列出所有进程<br>
  -o 自定义输出字段 我们设定显示字段为 stat(状态), ppid(进程父id), pid(进程id),cmd(命令)这四个参数</pre>
<pre>状态为 z或者Z 的进程为僵尸进程,所以我们使用grep抓取stat状态为zZ进程</pre>
<pre></pre>
<pre>      运行结果如下:</pre>
<img alt="" data-media-type="image" data-attr-org-src-id="2C1D0784850F48EAB8911F2DF7FBD2F9" data-attr-org-img-file="file:///C:/Users/lizhe/AppData/Local/YNote/data/sina2166797151/3e5588f268e94b58899759c8709571d8/clipboard.png">
<pre>    这时,可以使用 <code>kill -HUP 5255 杀掉这个进程。如果再次查看僵尸进程还存在,可以kill -HUP 5253(父进程)。</code></pre>
<pre><code>    如果有多个僵尸进程,可以通过</code></pre>
<pre>    ps -A -ostat,ppid,pid,cmd | grep -e '^'|awk 'print{$2}'|xargs kill -9</pre>
<pre>    处理。</pre>
</div>
</div>
<span style="font-size: medium"><strong><br></strong></span></div>
<div><br><hr><span style="font-size: medium"><strong>多进程--进程间通信(IPC)</strong></span></div>
<div><span style="font-size: medium"><strong>&nbsp;</strong></span></div>
<div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">管道用于承载进程之间的通讯数据。为了方便理解,可以将管道比作文件,进程A将数据写到管道P中,然后进程B从管道P中读取数据。php提供的管道操作 API与操作文件的API基本一样,除了创建管道使用posix_mkfifo函数,读写等操作均与文件操作函数相同。当然,你可以直接使用文件模拟管 道,但是那样无法使用管道的特性了。</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp;</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium"><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp; &nbsp; &nbsp; &nbsp; 管道的例子:</span></span>
<div><strong>本文部分来自网络参考,部分自己总结,由于一直保存在笔记中,并没有记录参考文章地址,如有侵权请通知删除。最近快被业务整疯了,这个等抽时间还需要好好的整理一番。</strong></div>
<div>&nbsp;</div>
<div><strong>多进程--fork</strong></div>
<div>
<p>场景:日常任务中,有时需要通过php脚本执行一些日志分析,队列处理等任务,当数据量比较大时,可以使用多进程来处理。</p>
<p>准备:php多进程需要pcntl,posix扩展支持,可以通过 php - m 查看,没安装的话需要重新编译php,加上参数<code>--enable-pcntl,posix一般默认会有。</code></p>
<p>注意:</p>
<p>   &nbsp;多进程实现只能在cli模式下,在web服务器环境下,会出现无法预期的结果,我测试<code>报错:<code>Call to undefined function: pcntl_fork()</code></code></p>
<p><code><code>&nbsp; &nbsp;</code></code>一个错误&nbsp;pcntl_fork causing “errno=32 Broken pipe” #474 ,看https://github.com/phpredis/phpredis/issues/474</p>




</div>
<p>&nbsp; &nbsp; &nbsp; &nbsp; <span style="background-color: rgba(255, 204, 153, 1)">注意两点:如果是在循环中创建子进程,那么子进程中最后要exit,防止子进程进入循环。</span><br><span style="background-color: rgba(255, 204, 153, 1)">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 子进程中的打开连接不能拷贝,使用的还是主进程的,需要用多例模式。</span></p>
<div>
<p>pcntl_fork:</p>
<p>  一次调用两次返回,在父进程中返回子进程pid,在子进程中返回0,出错返回-1。</p>
<p>&nbsp;pcntl_wait&nbsp;( int &amp;$status [, int $options ] ):</p>
<p>   &nbsp;阻塞当前进程,直到任意一个子进程退出或收到一个结束当前进程的信号,注意是结束当前进程的信号,子进程结束发送的SIGCHLD不算。使用$status返回子进程的状态码,并可以指定第二个参数来说明是否以阻塞状态调用</p>
<p>&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;阻塞方式调用的,函数返回值为子进程的pid,如果没有子进程返回值为-1;</p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; 非阻塞方式调用,函数还可以在有子进程在运行但没有结束的子进程时返回0。</p>





</div>
<div>pcntl_waitpid ( int $pid , int &amp;$status [, int $options ] )</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;功能同pcntl_wait,区别为waitpid为等待指定pid的子进程。当pid为-1时pcntl_waitpid与pcntl_wait 一样。在pcntl_wait和pcntl_waitpid两个函数中的$status中存了子进程的状态信息。</div>
<div>&nbsp;</div>
<div>检测是否是cli模式</div>
<div>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">* 确保这个函数只能运行在SHELL中 </span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 128, 128, 1)">substr</span>(<span style="color: rgba(0, 128, 128, 1)">php_sapi_name</span>(), 0, 3) !== 'cli'<span style="color: rgba(0, 0, 0, 1)">) {
  </span><span style="color: rgba(0, 0, 255, 1)">die</span>("cli mode only"<span style="color: rgba(0, 0, 0, 1)">);
}</span></pre>
</div>
<p>&nbsp;</p>
</div>
<div>SHELL脚本实现多进程(Qbus的多进程就是这样实现的,只不过用上了nohup 和 &amp; 改为守护进程):</div>
<div>
<div>
<div class="cnblogs_code">
<pre>#!/bin/<span style="color: rgba(0, 0, 0, 1)">bash

</span><span style="color: rgba(0, 0, 255, 1)">for</span>((i=<span style="color: rgba(128, 0, 128, 1)">1</span>;i&lt;=<span style="color: rgba(128, 0, 128, 1)">8</span>;i++<span style="color: rgba(0, 0, 0, 1)">))
</span><span style="color: rgba(0, 0, 255, 1)">do</span>   
    /usr/local/bin/php multiprocessTest.php &amp;<span style="color: rgba(0, 0, 0, 1)">
done

wait</span></pre>
</div>
</div>
<p>上面的shell程序,列了一个很简单的多进程程序,用一个for循环,实现了8进程并发来跑multiprocessTest.php这个程序。最后的wait语句,也可以使主进程,再等待所有进程都执行完后再往下执行的需求。</p>
<p>这个程序是没有问题的,很多现有的代码也都这样实现,但是这个程序的并发数是不可控的,即我们无法根据机器的核数去调度每一个进程的开关。</p>
<p>若我们的机器有8核或者更多,上面的程序是没有问题的,所有核都能充分利用,并且互相之间,没有争抢资源的情况出现。</p>
<p>但我们的机器要没有8核的话会是什么情况,同一时间运行的进程数多于核数,那么系统就会出现进程分配调度的问题,争抢资源也跟着相应而来,一个进程不能保证独立连续的执行,所有的进程运行会听从系统的调度,这样就会有更多的不确定因素出现。</p>
</div>
<div>一个始终保持固定个数的子进程在跑的例子</div>
<div>
<div class="cnblogs_code">
<pre>&lt;?<span style="color: rgba(0, 0, 0, 1)">php

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">最大的子进程数量</span>
<span style="color: rgba(128, 0, 128, 1)">$maxChildPro</span> = 8<span style="color: rgba(0, 0, 0, 1)">;

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">当前的子进程数量</span>
<span style="color: rgba(128, 0, 128, 1)">$curChildPro</span> = 0<span style="color: rgba(0, 0, 0, 1)">;

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">当子进程退出时,会触发该函数,当前子进程数-1</span>
<span style="color: rgba(0, 0, 255, 1)">function</span> sig_handler(<span style="color: rgba(128, 0, 128, 1)">$sig</span><span style="color: rgba(0, 0, 0, 1)">)
{
    </span><span style="color: rgba(0, 0, 255, 1)">global</span> <span style="color: rgba(128, 0, 128, 1)">$curChildPro</span><span style="color: rgba(0, 0, 0, 1)">;
    </span><span style="color: rgba(0, 0, 255, 1)">switch</span> (<span style="color: rgba(128, 0, 128, 1)">$sig</span><span style="color: rgba(0, 0, 0, 1)">) {
      </span><span style="color: rgba(0, 0, 255, 1)">case</span> SIGCHLD:
            <span style="color: rgba(0, 0, 255, 1)">echo</span> 'SIGCHLD', <span style="color: rgba(255, 0, 255, 1)">PHP_EOL</span><span style="color: rgba(0, 0, 0, 1)">;
            </span><span style="color: rgba(128, 0, 128, 1)">$curChildPro</span>--<span style="color: rgba(0, 0, 0, 1)">;
            </span><span style="color: rgba(0, 0, 255, 1)">break</span><span style="color: rgba(0, 0, 0, 1)">;
    }
}

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">配合pcntl_signal使用,简单的说,是为了让系统产生时间云,让信号捕捉函数能够捕捉到信号量</span>
<span style="color: rgba(0, 0, 255, 1)">declare</span>(ticks = 1<span style="color: rgba(0, 0, 0, 1)">);

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">注册子进程退出时调用的函数。SIGCHLD:在一个进程终止或者停止时,将SIGCHLD信号发送给其父进程。</span>
pcntl_signal(SIGCHLD, "sig_handler"<span style="color: rgba(0, 0, 0, 1)">);

</span><span style="color: rgba(0, 0, 255, 1)">while</span> (<span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">) {
    </span><span style="color: rgba(128, 0, 128, 1)">$curChildPro</span>++<span style="color: rgba(0, 0, 0, 1)">;
    </span><span style="color: rgba(128, 0, 128, 1)">$pid</span> =<span style="color: rgba(0, 0, 0, 1)"> pcntl_fork();
    </span><span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(128, 0, 128, 1)">$pid</span><span style="color: rgba(0, 0, 0, 1)">) {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">父进程运行代码,达到上限时父进程阻塞等待任一子进程退出后while循环继续</span>
      <span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(128, 0, 128, 1)">$curChildPro</span> &gt;= <span style="color: rgba(128, 0, 128, 1)">$maxChildPro</span><span style="color: rgba(0, 0, 0, 1)">) {
            pcntl_wait(</span><span style="color: rgba(128, 0, 128, 1)">$status</span><span style="color: rgba(0, 0, 0, 1)">);
      }
    } </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">子进程运行代码</span>
      <span style="color: rgba(128, 0, 128, 1)">$s</span> = <span style="color: rgba(0, 128, 128, 1)">rand</span>(2, 6<span style="color: rgba(0, 0, 0, 1)">);
      </span><span style="color: rgba(0, 128, 128, 1)">sleep</span>(<span style="color: rgba(128, 0, 128, 1)">$s</span><span style="color: rgba(0, 0, 0, 1)">);
      </span><span style="color: rgba(0, 0, 255, 1)">echo</span> "child sleep <span style="color: rgba(128, 0, 128, 1)">$s</span> second quit", <span style="color: rgba(255, 0, 255, 1)">PHP_EOL</span><span style="color: rgba(0, 0, 0, 1)">;
      </span><span style="color: rgba(0, 0, 255, 1)">exit</span><span style="color: rgba(0, 0, 0, 1)">;
    }
}</span></pre>
</div>
</div>
<div>
<p>&nbsp;</p>
</div>
<div>一个使用waitpid函数等待全部子进程退出,防止僵尸进程的例子</div>
<div>
<div class="cnblogs_code">
<pre>&lt;?<span style="color: rgba(0, 0, 0, 1)">php

</span><span style="color: rgba(128, 0, 128, 1)">$childs</span> = <span style="color: rgba(0, 0, 255, 1)">array</span><span style="color: rgba(0, 0, 0, 1)">();

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Fork10个子进程</span>
<span style="color: rgba(0, 0, 255, 1)">for</span> (<span style="color: rgba(128, 0, 128, 1)">$i</span> = 0; <span style="color: rgba(128, 0, 128, 1)">$i</span> &lt; 10; <span style="color: rgba(128, 0, 128, 1)">$i</span>++<span style="color: rgba(0, 0, 0, 1)">) {
    </span><span style="color: rgba(128, 0, 128, 1)">$pid</span> =<span style="color: rgba(0, 0, 0, 1)"> pcntl_fork();
    </span><span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(128, 0, 128, 1)">$pid</span> == -1<span style="color: rgba(0, 0, 0, 1)">)
      </span><span style="color: rgba(0, 0, 255, 1)">die</span>('Could not fork'<span style="color: rgba(0, 0, 0, 1)">);

    </span><span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(128, 0, 128, 1)">$pid</span><span style="color: rgba(0, 0, 0, 1)">) {
      </span><span style="color: rgba(0, 0, 255, 1)">echo</span> "parent \n"<span style="color: rgba(0, 0, 0, 1)">;
      </span><span style="color: rgba(128, 0, 128, 1)">$childs</span>[] = <span style="color: rgba(128, 0, 128, 1)">$pid</span><span style="color: rgba(0, 0, 0, 1)">;
    } </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Sleep $i+1 (s). 子进程可以得到$i参数</span>
      <span style="color: rgba(0, 128, 128, 1)">sleep</span>(<span style="color: rgba(128, 0, 128, 1)">$i</span> + 1<span style="color: rgba(0, 0, 0, 1)">);

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 子进程需要exit,防止子进程也进入for循环</span>
      <span style="color: rgba(0, 0, 255, 1)">exit</span><span style="color: rgba(0, 0, 0, 1)">();
    }
}

</span><span style="color: rgba(0, 0, 255, 1)">while</span> (<span style="color: rgba(0, 128, 128, 1)">count</span>(<span style="color: rgba(128, 0, 128, 1)">$childs</span>) &gt; 0<span style="color: rgba(0, 0, 0, 1)">) {
    </span><span style="color: rgba(0, 0, 255, 1)">foreach</span> (<span style="color: rgba(128, 0, 128, 1)">$childs</span> <span style="color: rgba(0, 0, 255, 1)">as</span> <span style="color: rgba(128, 0, 128, 1)">$key</span> =&gt; <span style="color: rgba(128, 0, 128, 1)">$pid</span><span style="color: rgba(0, 0, 0, 1)">) {
      </span><span style="color: rgba(128, 0, 128, 1)">$res</span> = pcntl_waitpid(<span style="color: rgba(128, 0, 128, 1)">$pid</span>, <span style="color: rgba(128, 0, 128, 1)">$status</span>,<span style="color: rgba(0, 0, 0, 1)"> WNOHANG);

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">-1代表error, 大于0代表子进程已退出,返回的是子进程的pid,非阻塞时0代表没取到退出子进程</span>
      <span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(128, 0, 128, 1)">$res</span> == -1 || <span style="color: rgba(128, 0, 128, 1)">$res</span> &gt; 0<span style="color: rgba(0, 0, 0, 1)">)
            </span><span style="color: rgba(0, 0, 255, 1)">unset</span>(<span style="color: rgba(128, 0, 128, 1)">$childs</span>[<span style="color: rgba(128, 0, 128, 1)">$key</span><span style="color: rgba(0, 0, 0, 1)">]);
    }

    </span><span style="color: rgba(0, 128, 128, 1)">sleep</span>(1<span style="color: rgba(0, 0, 0, 1)">);
}</span></pre>
</div>
<p>&nbsp;</p>
</div>
<div>一个实际的例子,php实现并发log拷贝</div>
<div>
<div class="cnblogs_code">
<pre>&lt;?<span style="color: rgba(0, 0, 0, 1)">php
</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> _fetchLog()
{
    </span><span style="color: rgba(128, 0, 128, 1)">$password</span>      = <span style="color: rgba(128, 0, 128, 1)">$this</span>-&gt;<span style="color: rgba(0, 0, 0, 1)">_getPassword();
    </span><span style="color: rgba(128, 0, 128, 1)">$online_log_path</span> = NginxConf::getArchiveDir(<span style="color: rgba(128, 0, 128, 1)">$this</span>-&gt;<span style="color: rgba(0, 0, 0, 1)">_stat_day);
    </span><span style="color: rgba(128, 0, 128, 1)">$task_log_path</span>   = QFrameConfig::getConfig('LOG_PATH'<span style="color: rgba(0, 0, 0, 1)">);
    </span><span style="color: rgba(128, 0, 128, 1)">$children</span>      = <span style="color: rgba(0, 0, 255, 1)">array</span><span style="color: rgba(0, 0, 0, 1)">();
    </span><span style="color: rgba(128, 0, 128, 1)">$success</span>         = <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;
    </span><span style="color: rgba(0, 0, 255, 1)">foreach</span>(<span style="color: rgba(128, 0, 128, 1)">$this</span>-&gt;_server_list <span style="color: rgba(0, 0, 255, 1)">as</span> <span style="color: rgba(128, 0, 128, 1)">$host</span> =&gt; <span style="color: rgba(128, 0, 128, 1)">$value</span><span style="color: rgba(0, 0, 0, 1)">)
    {
      </span><span style="color: rgba(128, 0, 128, 1)">$local_dir</span> = <span style="color: rgba(128, 0, 128, 1)">$this</span>-&gt;_prepareLocalDir(<span style="color: rgba(128, 0, 128, 1)">$host</span><span style="color: rgba(0, 0, 0, 1)">);
      </span><span style="color: rgba(128, 0, 128, 1)">$task_log</span>= "<span style="color: rgba(128, 0, 128, 1)">$task_log_path</span>/fetch_log.<span style="color: rgba(128, 0, 128, 1)">$host</span>"<span style="color: rgba(0, 0, 0, 1)">;
      </span><span style="color: rgba(128, 0, 128, 1)">$cmd</span> = "sshpass -p <span style="color: rgba(128, 0, 128, 1)">$password</span> rsync -av -e 'ssh -o StrictHostKeyChecking=no' <span style="color: rgba(128, 0, 128, 1)">$host</span>:<span style="color: rgba(128, 0, 128, 1)">$online_log_path</span>/* <span style="color: rgba(128, 0, 128, 1)">$local_dir</span> &gt;&gt; <span style="color: rgba(128, 0, 128, 1)">$task_log</span> 2&gt;&amp;1"<span style="color: rgba(0, 0, 0, 1)">;
      </span><span style="color: rgba(128, 0, 128, 1)">$pid</span> =<span style="color: rgba(0, 0, 0, 1)"> pcntl_fork();
      </span><span style="color: rgba(0, 0, 255, 1)">if</span>(-1 === <span style="color: rgba(128, 0, 128, 1)">$pid</span><span style="color: rgba(0, 0, 0, 1)">)
      {
            LogSvc</span>::<span style="color: rgba(0, 128, 128, 1)">log</span>('stat_pv_by_citycode_error', 'could not fork'<span style="color: rgba(0, 0, 0, 1)">);
            </span><span style="color: rgba(0, 0, 255, 1)">exit</span>('could not fork'<span style="color: rgba(0, 0, 0, 1)">);
      }
      </span><span style="color: rgba(0, 0, 255, 1)">else</span> <span style="color: rgba(0, 0, 255, 1)">if</span>(0 === <span style="color: rgba(128, 0, 128, 1)">$pid</span><span style="color: rgba(0, 0, 0, 1)">)
      {
            </span><span style="color: rgba(0, 128, 128, 1)">system</span>(<span style="color: rgba(128, 0, 128, 1)">$cmd</span>, <span style="color: rgba(128, 0, 128, 1)">$return_value</span><span style="color: rgba(0, 0, 0, 1)">);
            </span><span style="color: rgba(0, 0, 255, 1)">if</span>(0 !== <span style="color: rgba(128, 0, 128, 1)">$return_value</span><span style="color: rgba(0, 0, 0, 1)">)
            {
                LogSvc</span>::<span style="color: rgba(0, 128, 128, 1)">log</span>('stat_pv_by_citycode_error', "rsync <span style="color: rgba(128, 0, 128, 1)">$host</span> error"<span style="color: rgba(0, 0, 0, 1)">);
            }
            </span><span style="color: rgba(0, 0, 255, 1)">exit</span>(<span style="color: rgba(128, 0, 128, 1)">$return_value</span><span style="color: rgba(0, 0, 0, 1)">);
      }
      </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)">
      {
            </span><span style="color: rgba(128, 0, 128, 1)">$children</span>[<span style="color: rgba(128, 0, 128, 1)">$pid</span>] = 1<span style="color: rgba(0, 0, 0, 1)">;
      }
    }
    </span><span style="color: rgba(0, 0, 255, 1)">while</span>(!<span style="color: rgba(0, 0, 255, 1)">empty</span>(<span style="color: rgba(128, 0, 128, 1)">$children</span><span style="color: rgba(0, 0, 0, 1)">))
    {
      </span><span style="color: rgba(128, 0, 128, 1)">$pid</span> = pcntl_waitpid(-1, <span style="color: rgba(128, 0, 128, 1)">$status</span>,<span style="color: rgba(0, 0, 0, 1)"> WNOHANG);
      </span><span style="color: rgba(0, 0, 255, 1)">if</span>(0 === <span style="color: rgba(128, 0, 128, 1)">$pid</span><span style="color: rgba(0, 0, 0, 1)">)
      {
            </span><span style="color: rgba(0, 128, 128, 1)">sleep</span>(1<span style="color: rgba(0, 0, 0, 1)">);
      }
      </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)">
      {
            </span><span style="color: rgba(0, 0, 255, 1)">if</span>(0 !== pcntl_wexitstatus(<span style="color: rgba(128, 0, 128, 1)">$status</span><span style="color: rgba(0, 0, 0, 1)">))
            {
                </span><span style="color: rgba(128, 0, 128, 1)">$success</span> = <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">;
            }
            </span><span style="color: rgba(0, 0, 255, 1)">unset</span>(<span style="color: rgba(128, 0, 128, 1)">$children</span>[<span style="color: rgba(128, 0, 128, 1)">$pid</span><span style="color: rgba(0, 0, 0, 1)">]);
      }
    }
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(128, 0, 128, 1)">$success</span><span style="color: rgba(0, 0, 0, 1)">;
}</span>&nbsp;</pre>
</div>
</div>
<div><hr></div>
<div>&nbsp;</div>
<div><span style="font-size: medium"><strong>多进程--信号</strong></span></div>
<div>&nbsp;</div>
<div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">同类信号只能存储一个,多余的自动丢弃</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">可以发送小于0的值代表发给全部子进程,包括自己,其实是一个组的进程。</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp;</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">PCNTL使用ticks来作为信号处理机制(signal handle callback mechanism),可以最小程度地降低处理异步事件时的负载。何谓ticks?Tick 是一个在代码段中解释器每执行 N 条低级语句就会发生的事件,这个代码段需要通过declare来指定。<br></span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp;</span></div>
<div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">    pcntl_signal ( int $signo , callback $handler [, bool $restart_syscalls ] ) //为某个SIG注册一个处理函数</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp; &nbsp; &nbsp;<code>posix_kill(posix_getpid(),&nbsp;SIGHUP);&nbsp;<code>为自己生成SIGHUP信号</code></code></span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium"><code><code>&nbsp; &nbsp; &nbsp; &nbsp;declare(ticks&nbsp;=&nbsp;1); //php &lt; 5.3</code></code></span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp; &nbsp; &nbsp;<strong>pcntl_signal_dispatch</strong>&nbsp;(&nbsp;void&nbsp;) &nbsp;</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp; &nbsp; &nbsp; &nbsp; 调用每个等待信号通过pcntl_signal()&nbsp;安装的处理器。说明一下:<code>pcntl_signal()</code>函数仅仅是注册信号和它的处理方法,真正接收到信号并调用其处理方法的是<code>pcntl_signal_dispatch()</code>函数</span><span style="font-family: 方正兰亭黑; font-size: medium; line-height: 1.5">必须在循环里调用,为了检测是否有新的信号等待dispatching。</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp; &nbsp; &nbsp;<code>pcntl_signal_dispatch()</code></span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">      这个函数是PHP 5.3以上才支持的,如果你的PHP版本大于5.3,建议使用这个方法调用信号处理器。5.3以下的版本需要在注册信号之前加一句:<code>declare(ticks = 1);</code>表示每执行一条低级指令,就检查一次信号,如果检测到注册的信号,就调用其信号处理器。</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp; &nbsp; &nbsp;pcntl_alarm ( int $seconds )</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp; &nbsp; &nbsp; &nbsp; 设置一个$seconds秒后发送SIGALRM信号的计数器</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">5.发送信号:</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp;</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp; &nbsp; posix_kill(): 向进程发送信号。&nbsp;</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp; &nbsp; SIGINT : 通过键盘CTRL+C.</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp; &nbsp; SIGTERM :&nbsp;有时候进程失去响应了还会执行<code>kill </code>命令,未加任何其他参数的话,程序会接收到一个SIGTERM信号。 &nbsp; &nbsp; &nbsp; &nbsp;</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;程序收到上面两个信号的时候,默认都会结束执行,可以通过注册信号改变默认行为。</span></div>
<div>&nbsp;</div>





</div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">一个注册信号处理器的例子,得到结论:</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">sleep函数会被信号唤醒,不再休眠,返回唤醒时剩余的秒数</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">对于说法同类信号只能存储一个,多余的自动丢弃,测试发现多次CTRL+C后,进程会先唤醒sleep,再唤醒usleep,当执行到pcntl_signal_dispatch时,会一次输出多个“SIGINT”,不知道存储一个是不是只的是什么地方。</span></div>
<div>
<div class="cnblogs_code">
<pre>&lt;?<span style="color: rgba(0, 0, 0, 1)">php

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 定义一个处理器,接收到SIGINT信号后只输出一行信息</span>
<span style="color: rgba(0, 0, 255, 1)">function</span> signalHandler(<span style="color: rgba(128, 0, 128, 1)">$signal</span><span style="color: rgba(0, 0, 0, 1)">)
{
    </span><span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(128, 0, 128, 1)">$signal</span> ==<span style="color: rgba(0, 0, 0, 1)"> SIGINT) {
      </span><span style="color: rgba(0, 0, 255, 1)">echo</span> 'SIGINT', <span style="color: rgba(255, 0, 255, 1)">PHP_EOL</span><span style="color: rgba(0, 0, 0, 1)">;
    }
}

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 信号注册:当接收到SIGINT信号时,调用signalHandler()函数</span>
pcntl_signal(SIGINT, 'signalHandler'<span style="color: rgba(0, 0, 0, 1)">);

</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">*
* PHP &lt; 5.3 使用
* 配合pcntl_signal使用,表示每执行一条低级指令,就检查一次信号,如果检测到注册的信号,就调用其信号处理器。
</span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">if</span> (!<span style="color: rgba(0, 128, 128, 1)">function_exists</span>("pcntl_signal_dispatch"<span style="color: rgba(0, 0, 0, 1)">)) {
    </span><span style="color: rgba(0, 0, 255, 1)">declare</span>(ticks=1<span style="color: rgba(0, 0, 0, 1)">);
}

</span><span style="color: rgba(0, 0, 255, 1)">while</span> (<span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">) {
    </span><span style="color: rgba(128, 0, 128, 1)">$s</span> = <span style="color: rgba(0, 128, 128, 1)">sleep</span>(10<span style="color: rgba(0, 0, 0, 1)">);
    </span><span style="color: rgba(0, 0, 255, 1)">echo</span> <span style="color: rgba(128, 0, 128, 1)">$s</span>, <span style="color: rgba(255, 0, 255, 1)">PHP_EOL</span>; <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">信号会唤醒sleep,返回剩余的秒数。

// do something</span>
    <span style="color: rgba(0, 0, 255, 1)">for</span> (<span style="color: rgba(128, 0, 128, 1)">$i</span> = 0; <span style="color: rgba(128, 0, 128, 1)">$i</span> &lt; 5; <span style="color: rgba(128, 0, 128, 1)">$i</span>++<span style="color: rgba(0, 0, 0, 1)">) {
      </span><span style="color: rgba(0, 0, 255, 1)">echo</span> <span style="color: rgba(128, 0, 128, 1)">$i</span> . <span style="color: rgba(255, 0, 255, 1)">PHP_EOL</span><span style="color: rgba(0, 0, 0, 1)">;
      </span><span style="color: rgba(0, 128, 128, 1)">usleep</span>(100000<span style="color: rgba(0, 0, 0, 1)">);
    }

    </span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">*
   * PHP &gt;= 5.3
   * 调用已安装的信号处理器
   * 必须在循环里调用,为了检测是否有新的信号等待dispatching。
   </span><span style="color: rgba(0, 128, 0, 1)">*/</span>
    <span style="color: rgba(0, 0, 255, 1)">if</span> (!<span style="color: rgba(0, 128, 128, 1)">function_exists</span>("pcntl_signal_dispatch"<span style="color: rgba(0, 0, 0, 1)">)) {
      pcntl_signal_dispatch();
    }

}</span></pre>
</div>
<p>&nbsp;</p>
</div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">一个隔5s发一个信号的例子,通过pcntl_alarm实现</span></div>
<div>
<div class="cnblogs_code">
<pre>&lt;?<span style="color: rgba(0, 0, 0, 1)">php

</span><span style="color: rgba(0, 0, 255, 1)">declare</span>(ticks = 1<span style="color: rgba(0, 0, 0, 1)">);

</span><span style="color: rgba(0, 0, 255, 1)">function</span> signal_handler(<span style="color: rgba(128, 0, 128, 1)">$signal</span><span style="color: rgba(0, 0, 0, 1)">) {
    </span><span style="color: rgba(0, 0, 255, 1)">print</span> "Caught SIGALRM\n"<span style="color: rgba(0, 0, 0, 1)">;
    pcntl_alarm(</span>5<span style="color: rgba(0, 0, 0, 1)">);
}

pcntl_signal(SIGALRM</span>, "signal_handler", <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">);
pcntl_alarm(</span>5<span style="color: rgba(0, 0, 0, 1)">);

</span><span style="color: rgba(0, 0, 255, 1)">for</span><span style="color: rgba(0, 0, 0, 1)">(;;) {
}</span></pre>
</div>
<p>&nbsp;</p>
</div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">一个通过发送信号杀死进程的例子,信号可以发给自己也可以发给其他进程。</span></div>
<div>
<div class="cnblogs_code">
<pre>&lt;?<span style="color: rgba(0, 0, 0, 1)">php

</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">*
* 父进程通过pcntl_wait等待子进程退出
* 子进程通过信号kill自己,也可以在父进程中发送kil信号结束子进程
</span><span style="color: rgba(0, 128, 0, 1)">*/</span>

<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">生成子进程</span>
<span style="color: rgba(128, 0, 128, 1)">$pid</span> =<span style="color: rgba(0, 0, 0, 1)"> pcntl_fork();
</span><span style="color: rgba(0, 0, 255, 1)">if</span>(<span style="color: rgba(128, 0, 128, 1)">$pid</span> == -1<span style="color: rgba(0, 0, 0, 1)">){
    </span><span style="color: rgba(0, 0, 255, 1)">die</span>('could not fork'<span style="color: rgba(0, 0, 0, 1)">);
}</span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)">{
    </span><span style="color: rgba(0, 0, 255, 1)">if</span>(<span style="color: rgba(128, 0, 128, 1)">$pid</span><span style="color: rgba(0, 0, 0, 1)">){
      </span><span style="color: rgba(128, 0, 128, 1)">$status</span> = 0<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">阻塞父进程,直到子进程结束,不适合需要长时间运行的脚本.
      //可使用pcntl_wait($status, WNOHANG)实现非阻塞式</span>
      pcntl_wait(<span style="color: rgba(128, 0, 128, 1)">$status</span><span style="color: rgba(0, 0, 0, 1)">);
      </span><span style="color: rgba(0, 0, 255, 1)">exit</span><span style="color: rgba(0, 0, 0, 1)">;
    }</span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)">{
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">结束当前子进程,以防止生成僵尸进程</span>
      <span style="color: rgba(0, 0, 255, 1)">if</span>(<span style="color: rgba(0, 128, 128, 1)">function_exists</span>("posix_kill"<span style="color: rgba(0, 0, 0, 1)">)){
            posix_kill(</span><span style="color: rgba(0, 128, 128, 1)">getmypid</span>(),<span style="color: rgba(0, 0, 0, 1)"> SIGTERM);
      }</span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)">{
            </span><span style="color: rgba(0, 128, 128, 1)">system</span>('kill -9'. <span style="color: rgba(0, 128, 128, 1)">getmypid</span><span style="color: rgba(0, 0, 0, 1)">());
      }
      </span><span style="color: rgba(0, 0, 255, 1)">exit</span><span style="color: rgba(0, 0, 0, 1)">;
    }
}</span></pre>
</div>
</div>
<hr></div>
<div>&nbsp;</div>
<div><span style="font-size: medium"><strong>多进程--僵尸进程</strong></span></div>
<div><span style="font-size: medium"><strong>&nbsp;</strong></span></div>
<div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">当子进程比父进程先退出,但是父进程还在运行中并且可能很长一段时间不会退出,子进程就变成了僵尸进程。然后内核会找到这个僵尸进程的PPID,给这个PPID发送一个SIGCHLD信号。如果父进程中没有通过pcntl_wait或者pcntl_waitpid函数处理,并且没有通过posix_kill发送退出信号给子进程,那么这个子进程就彻底僵尸了。</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ps aux查看到时Z(zombie)状态。</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 一般需要在父进程结束前回收子进程先,pcntl_wait()函数会将父进程挂起,直到一个子进程退出。</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 如果父进程先挂了,子进程会被1号进程接管,当子进程结束时1号进程会自动回收。所以关闭僵尸进程的另一种方法就是关闭他们的父进程。</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 子进程如何得知父进程退出:</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;1. 当父进程退出时,会有一个INIT进程来领养这个子进程。这个INIT进程的进程号为1,所以子进程可以通过使用getppid()来取得当前父进程的pid。如果返回的是1,表明父进程已经变为INIT进程,则原进程已经推出。</span>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp;</span></div>
<span style="font-family: 方正兰亭黑; font-size: medium">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 2. 使用kill函数,php中是posix_kill,向原有的父进程发送空信号(kill(pid, 0))。使用这个方法对某个进程的存在性进行检查,而不会真的发送信号。所以,如果这个函数返回-1表示父进程已经退出。</span></div>
<div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp;</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 僵尸进程:</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;<span style="font-family: 微软雅黑; font-size: medium">在UNIX 系统中,一个进程结束了,但是他的父进程没有等待(调用wait / waitpid)他,那么他将变成一个僵尸进程。</span></span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium"><span style="font-family: 微软雅黑; font-size: medium">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;<span style="font-family: 微软雅黑; font-size: medium">僵尸进程是一个早已死亡的进程,但在进程表 (processs table)中仍占了一个位置(slot)。</span></span></span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium"><span style="font-family: 微软雅黑; font-size: medium"><span style="font-family: 微软雅黑; font-size: medium">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;僵尸进程不及时回收,会在系统中占用一个进程表项,如果这种僵尸进程过多,最后系统就没有可以用的进程表项,于是也无法再运行其它的程序。</span></span></span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 任何进程在退出前(exit退出)都会变为僵尸进程。用于保存进程的状态等信息。为什么呢?</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;<span style="font-family: 微软雅黑; font-size: medium">子进程的结束和父进程的运行是一个异步过程,即父进程永远无法预测子进程到底什么时候结束。那么 会不会因为父进程太忙来不及 wait 子进程,或者说不知道子进程什么时候结束,而丢失子进程结束时的状态信息呢?不会。因为UNIX提供了一种机制可以保证,只要父进程想知道子进程结束时的 状态信息,就可以得到。这种机制就是:当子进程走完了自己的生命周期后,它会执行exit()系统调用,内核释放该进程所有的资源,包括打开的文件,占用 的内存等。但是仍然为其保留一定的信息(包括进程号the process ID,退出码exit code,退出状态the terminationstatus of the process,运行时间the amount of CPU time taken by the process等),这些数据会一直保留到系统将它传递给它的父进程为止,直到父进程通过wait / waitpid来取时才释放。</span></span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 预防僵尸进程</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium"><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</span></span>
<p><span style="font-family: 微软雅黑; font-size: medium">(1) 父进程通过wait和waitpid等函数等待子进程结束,这会导致父进程挂起。它不适合子进程需要长时间运行的情况(会导致超时)。&nbsp;&nbsp; &nbsp; &nbsp;</span></p>
<p><span style="font-family: 微软雅黑; font-size: medium">&nbsp;&nbsp;&nbsp; 执行wait()或waitpid()系统调用,则子进程在终止后会立即把它在进程表中的数据返回给父进程,此时系统会立即删除该进入点。在这种情形下就不会产生defunct进程。</span></p>
<p><span style="font-family: 微软雅黑; font-size: medium">&nbsp;&nbsp;&nbsp; (2) 如果父进程很忙,那么可以用signal函数为SIGCHLD安装handler。在子进程结束后,父进程会收到该信号,可以在handler中调用wait回收。</span></p>
<p><span style="font-family: 微软雅黑; font-size: medium">&nbsp;&nbsp;&nbsp; (3) 如果父进程不关心子进程什么时候结束,那么可以用signal(SIGCLD, SIG_IGN)或signal(SIGCHLD, SIG_IGN)通知内核,自己对子进程的结束不感兴趣,那么子进程结束后,内核会回收,并不再给父进程发送信号</span></p>
<p><span style="font-family: 微软雅黑; font-size: medium">&nbsp;&nbsp;&nbsp; (4)fork两次,父进程fork一个子进程,然后继续工作,子进程fork一个孙进程后退出,那么孙进程被init接管,孙进程结束后,init会回收。不过子进程的回收还要自己做。</span></p>
</div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 查看和清零僵尸进程:测试发现通过 ps -ef 和 aux 是不能看到僵尸进程的。</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 需要通过top命令实时看到当前系统的僵尸进程个数。</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</span><img alt="" data-media-type="image" data-attr-org-src-id="A6126D60D955486EBB3A5DADA9513C07" data-attr-org-img-file="file:///C:/Users/lizhe/AppData/Local/YNote/data/sina2166797151/111faca45e8240b7baeea9761b1d77dc/clipboard.png"></div>
<div>&nbsp;</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 用ps命令查看僵尸进程:</div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;
<pre>            ps -A -ostat,ppid,pid,cmd | grep -e '^'</pre>
<pre></pre>
<pre>         命令注解:<br>
  -A 参数列出所有进程<br>
  -o 自定义输出字段 我们设定显示字段为 stat(状态), ppid(进程父id), pid(进程id),cmd(命令)这四个参数</pre>
<pre>状态为 z或者Z 的进程为僵尸进程,所以我们使用grep抓取stat状态为zZ进程</pre>
<pre></pre>
<pre>      运行结果如下:</pre>
<img alt="" data-media-type="image" data-attr-org-src-id="2C1D0784850F48EAB8911F2DF7FBD2F9" data-attr-org-img-file="file:///C:/Users/lizhe/AppData/Local/YNote/data/sina2166797151/3e5588f268e94b58899759c8709571d8/clipboard.png">
<pre>    这时,可以使用 <code>kill -HUP 5255 杀掉这个进程。如果再次查看僵尸进程还存在,可以kill -HUP 5253(父进程)。</code></pre>
<pre><code>    如果有多个僵尸进程,可以通过</code></pre>
<pre>    ps -A -ostat,ppid,pid,cmd | grep -e '^'|awk 'print{$2}'|xargs kill -9</pre>
<pre>    处理。</pre>
</div>
</div>
<span style="font-size: medium"><strong><br></strong></span></div>
<div><br><hr><span style="font-size: medium"><strong>多进程--进程间通信(IPC)</strong></span></div>
<div><span style="font-size: medium"><strong>&nbsp;</strong></span></div>
<div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">管道用于承载进程之间的通讯数据。为了方便理解,可以将管道比作文件,进程A将数据写到管道P中,然后进程B从管道P中读取数据。php提供的管道操作 API与操作文件的API基本一样,除了创建管道使用posix_mkfifo函数,读写等操作均与文件操作函数相同。当然,你可以直接使用文件模拟管 道,但是那样无法使用管道的特性了。</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp;</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">&nbsp; &nbsp; &nbsp; &nbsp; 管道的例子:http://www.cnblogs.com/bourneli/archive/2012/07/06/2579893.html</span></div>





<span style="font-size: medium"><strong><br></strong></span></div>
<div>&nbsp;</div>
<div><hr><span style="font-size: medium"><strong>多进程--守护进程</strong></span></div>
<div>&nbsp;</div>
<div>nohup &nbsp;守护进程 &nbsp;</div>
<div>项目的Qbus实现</div>
<div>百度nohup</div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">百度守护进程</span></div>
<div><span style="font-family: 方正兰亭黑; font-size: medium">php写守护进程:</span>http://blog.csdn.net/tengzhaorong/article/details/9764655</div>
<div><hr><span style="font-size: medium"><strong>多进程--socket实现简单TCP server</strong></span></div>
<div><span style="font-size: medium"><strong>&nbsp;</strong></span></div>
<div><span style="font-size: medium">韩天峰:</span>http://rango.swoole.com/archives/48</div>
<div>&nbsp;</div>
<div>上边的是接收端,可以把Qframe中的发送写上,,测试成功否。</div>
<div>
<div class="cnblogs_code">
<pre>&lt;?<span style="color: rgba(0, 0, 0, 1)">php

</span><span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">function</span> sendSDKMsg(<span style="color: rgba(128, 0, 128, 1)">$version</span><span style="color: rgba(0, 0, 0, 1)">)
{</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">{{{</span><span style="color: rgba(0, 128, 0, 1)">*/</span>
    <span style="color: rgba(0, 0, 255, 1)">if</span>(!self::sendRandChance(self::EA_LAST_TIME_KEY.":".<span style="color: rgba(128, 0, 128, 1)">$version</span>)) <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">;

    </span><span style="color: rgba(128, 0, 128, 1)">$fp</span> = @<span style="color: rgba(0, 128, 128, 1)">fsockopen</span>( "udp://".self::UDP_HOST , self::UDP_PORT , <span style="color: rgba(128, 0, 128, 1)">$errno</span><span style="color: rgba(0, 0, 0, 1)"> );
    </span><span style="color: rgba(0, 0, 255, 1)">if</span>( !<span style="color: rgba(128, 0, 128, 1)">$fp</span> ) <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">;
    </span><span style="color: rgba(0, 128, 128, 1)">stream_set_timeout</span>( <span style="color: rgba(128, 0, 128, 1)">$fp</span> , 0 , 100<span style="color: rgba(0, 0, 0, 1)"> );
    </span><span style="color: rgba(0, 128, 128, 1)">stream_set_blocking</span>( <span style="color: rgba(128, 0, 128, 1)">$fp</span> , 0<span style="color: rgba(0, 0, 0, 1)"> );

    </span><span style="color: rgba(128, 0, 128, 1)">$sysinfo</span>    =<span style="color: rgba(0, 0, 0, 1)"> posix_uname();
    </span><span style="color: rgba(128, 0, 128, 1)">$msg</span>      = <span style="color: rgba(128, 0, 128, 1)">$version</span>." - ".<span style="color: rgba(128, 0, 128, 1)">$sysinfo</span>['nodename']." - ".<span style="color: rgba(0, 128, 128, 1)">date</span>('Y-m-d H:i:s',<span style="color: rgba(0, 128, 128, 1)">time</span><span style="color: rgba(0, 0, 0, 1)">());
    </span><span style="color: rgba(128, 0, 128, 1)">$res</span>      = <span style="color: rgba(0, 128, 128, 1)">fwrite</span>( <span style="color: rgba(128, 0, 128, 1)">$fp</span> , <span style="color: rgba(128, 0, 128, 1)">$msg</span><span style="color: rgba(0, 0, 0, 1)"> );
    </span><span style="color: rgba(0, 128, 128, 1)">fclose</span>(<span style="color: rgba(128, 0, 128, 1)">$fp</span><span style="color: rgba(0, 0, 0, 1)">);
}</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">}}}</span><span style="color: rgba(0, 128, 0, 1)">*/</span>

<span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">function</span> sendRandChance(<span style="color: rgba(128, 0, 128, 1)">$key</span><span style="color: rgba(0, 0, 0, 1)">)
{</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">{{{</span><span style="color: rgba(0, 128, 0, 1)">*/</span>
    <span style="color: rgba(128, 0, 128, 1)">$now</span> = <span style="color: rgba(0, 128, 128, 1)">microtime</span>(<span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">);

    </span><span style="color: rgba(0, 0, 255, 1)">if</span>(<span style="color: rgba(0, 128, 128, 1)">function_exists</span>("eaccelerator_get"<span style="color: rgba(0, 0, 0, 1)">))
    {
      </span><span style="color: rgba(128, 0, 128, 1)">$lastInserTime</span> = eaccelerator_get(<span style="color: rgba(128, 0, 128, 1)">$key</span><span style="color: rgba(0, 0, 0, 1)">);
      </span><span style="color: rgba(0, 0, 255, 1)">if</span>(!<span style="color: rgba(128, 0, 128, 1)">$lastInserTime</span>) <span style="color: rgba(128, 0, 128, 1)">$lastInserTime</span> = 0<span style="color: rgba(0, 0, 0, 1)">;

      </span><span style="color: rgba(0, 0, 255, 1)">if</span>( (<span style="color: rgba(128, 0, 128, 1)">$now</span> - <span style="color: rgba(128, 0, 128, 1)">$lastInserTime</span>) &lt; self::SEND_INTERVAL ) <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">;
      eaccelerator_put(</span><span style="color: rgba(128, 0, 128, 1)">$key</span>, <span style="color: rgba(128, 0, 128, 1)">$now</span><span style="color: rgba(0, 0, 0, 1)">);
      </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;
    }</span><span style="color: rgba(0, 0, 255, 1)">else</span> <span style="color: rgba(0, 0, 255, 1)">if</span>(<span style="color: rgba(0, 128, 128, 1)">function_exists</span>("apc_fetch"<span style="color: rgba(0, 0, 0, 1)">))
    {
      </span><span style="color: rgba(128, 0, 128, 1)">$lastInserTime</span> = apc_fetch(<span style="color: rgba(128, 0, 128, 1)">$key</span><span style="color: rgba(0, 0, 0, 1)">);
      </span><span style="color: rgba(0, 0, 255, 1)">if</span>(!<span style="color: rgba(128, 0, 128, 1)">$lastInserTime</span>) <span style="color: rgba(128, 0, 128, 1)">$lastInserTime</span> = 0<span style="color: rgba(0, 0, 0, 1)">;

      </span><span style="color: rgba(0, 0, 255, 1)">if</span>( (<span style="color: rgba(128, 0, 128, 1)">$now</span> - <span style="color: rgba(128, 0, 128, 1)">$lastInserTime</span>) &lt; self::SEND_INTERVAL ) <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">;
      apc_store(</span><span style="color: rgba(128, 0, 128, 1)">$key</span>, <span style="color: rgba(128, 0, 128, 1)">$now</span><span style="color: rgba(0, 0, 0, 1)">);
      </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;
    }

    </span><span style="color: rgba(128, 0, 128, 1)">$rand</span> = <span style="color: rgba(0, 128, 128, 1)">rand</span>(1,60<span style="color: rgba(0, 0, 0, 1)">);
    </span><span style="color: rgba(0, 0, 255, 1)">if</span>((<span style="color: rgba(0, 128, 128, 1)">time</span>()%60 == <span style="color: rgba(128, 0, 128, 1)">$rand</span>) &amp;&amp; <span style="color: rgba(0, 128, 128, 1)">rand</span>(0,20) == 3<span style="color: rgba(0, 0, 0, 1)">)
    {
      </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;
    }
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">;
}</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">}}}</span><span style="color: rgba(0, 128, 0, 1)">*/</span></pre>
</div>
<p>&nbsp;</p>
</div>
<div>&nbsp;</div>
<div>&nbsp;</div>
<div>
<div>
<div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;</div>
</div>
</div>
<div>&nbsp;</div>
</div>
</div>
</div>
</div>

</div>
<div id="MySignature" role="contentinfo">
    <div id="fry_added_part">
    <div class="fry_website">
      <div class="fry_website" style="color:#3c8dbc;padding: 10px 0;display:none;">
            我的旨在学过的东西不再忘记(主要使用艾宾浩斯遗忘曲线算法及其它智能学习复习算法)的偏公益性质的完全免费的编程视频学习网站:
            【读书编程笔记】fanrenyi.com;有各种前端、后端、算法、大数据、人工智能等课程。
      </div>
      <div class="copyright" style="color: red;padding-bottom: 10px;">
            版权申明:欢迎转载,但请注明出处
            <div style="font-size: 12px">一些博文中有一些参考内容因时间久远找不到来源了没有注明,如果侵权请联系我删除。</div>
      </div>
      <div id="fry_know_friends" style="color: red;padding-bottom: 10px;font-size: 22px;">
            <div style="display:none;"></div>
            <div style="display:none;">在校每年国奖、每年专业第一,加拿大留学,先后工作于华东师范大学和香港教育大学。</div>
            <div style="display:none;">2025-04-30:宅加太忙,特此在网上找女朋友,坐标上海,非诚勿扰,vx:fan404006308</div>
      </div>
      <div class="copyright" style="color: green;padding-bottom: 10px;">
            录播课资料github地址:https://github.com/fry404006308/fry_course_materials
      </div>
      <div style="display:none;"></div>
    </div>
    <div class="fry_tech_group" style="color: mediumpurple;">
      <div>
            AI交流资料群:753014672
      </div>
    </div>
    <div class="fry_recommend">
      <h2>作者相关推荐</h2>
      <div id="fry_recommend" style="padding-bottom: 40px">
      </div>
    </div>
    <style>
      #fry_added_part .inspiration_content{
            //max-height: 120px;
            overflow: auto;
            margin: 20px 0;
      }
      #fry_added_part .fry_inspiration .simple a{
            margin-right: 25px;
      }
    </style>
    <div style="display:none;" class="fry_inspiration">
      <div style="color: red;font-size: 20px;text-align: center;" class="title">
            感悟总结
      </div>
      <!--分为详细部分和简略部分-->
      <!--详细部分-->
      <div class="detail">
            <div class="url_set">
                <div style="display: none;" class="per_url" href="https://www.cnblogs.com/Renyi-Fan/p/13498246.html"></div>
                <!--<div style="display: none;" class="per_url" href="https://www.cnblogs.com/Renyi-Fan/p/14379366.html"></div>-->
                <!--<div style="display: none;" class="per_url" href="https://www.cnblogs.com/Renyi-Fan/p/14154624.html"></div>-->
            </div>
            <div class="content_set"></div>
      </div>
      <!--简略部分-->
      <div style="display: none;" class="simple">
            <div style="color: #FF9966;margin-bottom: 10px;">其它重要感悟总结</div>
            <div>
                感悟总结200813
                最近心境200830
                最近心境201019
                201218-210205
            </div>
      </div>
    </div>
</div><br><br>
来源:https://www.cnblogs.com/Renyi-Fan/p/10909584.html
頁: [1]
查看完整版本: php中如何实现多进程