php在pcntl_
fork()后生成的
子进程(通常为僵尸进
程)必须由pcntl
_waitpid()
函数进行资源释放。但
在pcntl_wait
pid()不一定释放
的就是当前运行的进程
,也可能是过去生成的
僵尸进程(没有释放)
;也可能是并发时其它
访问者的僵尸进程。但
可以 使用posix_ki
ll($cid, SIGTERM)在子
进程结束时杀掉它。
子进程会自动复制父进 程空间里的变量。
PHP多进程编程示例 2
如果不需要阻塞进程, 而又想得到子进程的退
出状态,则可以注释掉
pcntl_wait
($status)语
句,或写成:
<?php
pcntl_wait ($status, 1);
//或
pcntl_wait ($status, WNOHANG);
在上面的代码中,如果 父进程退出(使用ex it函数退出或red irect),则会导 致子进程成为僵尸进程 (会交给init进程 控制),子进程不再执 行。
僵 尸进程是指的父进程已 经退出,而该进程de ad之后没有进程接受 ,就成为僵尸进程.( zombie)进程。 任何进程在退出前(使 用exit退出)都会 变成 僵尸进程(用于保存进 程的状态等信息),然 后由init进程接管 。如果不及时回收僵尸 进程,那么它在系统中 就会占用一个进程表项 ,如果这种僵尸进程过 多, 最后系统就没有可以用 的进程表项,于是也无 法再运行其它的程序。
预防僵尸进程有以下几 种方法:
//.....
//需要安装pcnt l的php扩展,并加 载它
if(function_e xists("pcntl_for k")){
//生成第一个子进程
$pid = pcntl_fork (); //$pid即所产生 的子进程id
if($pid == -1){
//子进程fork失 败
die('could not fork');
}else{
if($pid){
//父进程code
sleep(5); //等待5秒
exit(0); //或$this-> _redirect( '/');
}else{
//第一个子进程co de
//产生孙进程
if(($gpid = pcntl_fork ()) < 0){ $gpid即 所产生的孙进程id
//孙进程产生失败
die('could not fork');
}elseif($gpid > 0){
//第一个子进程co de,即孙进程的父进 程
$status = 0;
$status = pcntl_wait ($status); //阻塞子进程,并返 回孙进程的退出状态, 用于检查是否正常退出
if($status ! = 0) file_put_c ontent('filename' , '孙进程异常退出');
//得到父进程id
//$ppid = posix_getp pid(); //如果$ppid为 1则表示其父进程已变 为init进程,原父 进程已退出
//得到子进程id: posix_getp id()或getmy pid()或是for k返回的变量$pid
//kill掉子进程
//posix_ki ll(getmypi d(), SIGTERM);
exit(0);
}else{ //即$gpid == 0
//孙进程code
//....
//结束孙进程(即当 前进程),以防止生成 僵尸进程
if(function_e xists('posix_kil l')){
posix_kill (getmypid(), SIGTERM);
}else{
system('kill -9'. getmypid());
}
exit(0);
}
}
}
}else{
// 不支持多进程处理时的 代码在这里
}
//.....
?>
怎样产生僵尸进程的
一 个进程在调用exit 命令结束自己的生命的
时候,其实它并没有真
正的被销毁,而是留下
一个称为僵尸进程(Z
ombie)的数据结
构(系统调用exit
,它 的作用是使进程退出,
但也仅仅限于将一个正
常的进程变成一个僵尸
进程,并不能将其完全
销毁)。在Linux
进程的状态中,僵尸进
程是非常特殊的一种,
它已 经放弃了几乎所有内存
空间,没有任何可执行
代码,也不能被调度,
仅仅在进程列表中保留
一个位置,记载该进程
的退出状态等信息供其
他进程收集,除此之外
,僵 尸进程不再占有任何内
存空间。它需要它的父
进程来为它收尸,如果
他的父进程没安装SI
GCHLD信号处理函
数调用wait或wa
itpid()等待子
进程 结束,又没有显式忽略
该信号,那么它就一直
保持僵尸状态,如果这
时父进程结束了,那么
init进程自动会接
手这个子进程,为它收
尸,它还是能被清除的
。但 是如果如果父进程是一
个循环,不会结束,那
么子进程就会一直保持
僵尸状态,这就是为什
么系统中有时会有很多
的僵尸进程。
任何一个子进程(in it除外)在exit()
之后,并非马上就消失
掉,而是留下一个称为
僵尸进程(Zombi
e)的数据结构,等待
父进程处理。这是每个
子进程在结束时都要经
过的 阶段。如果子进程在e
xit()之后,父进
程没有来得及处理,这
时用ps命令就能看到
子进程的状态是”Z”
。如果父进程能及时 处理,可能用ps命令
就来不及看到子进程的
僵尸状态,但这并不等
于子进程不经过僵尸状
态。
如果父进程在子进程结 束之前退出,则子进程
将由init接管。i
nit将会以父进程的
身份对僵尸状态的子进
程进行处理。
另外,还可以写一个p hp文件,然后在以后
台形式来运行它,例如
:
<?php
//Action代码
public function createActi on(){
//....
//将args替换成 要传给insertL argeData.p hp的参数,参数间用 空格间隔
system('php -f insertLarg eData.php ' . ' args ' . '&');
$this->redirect('/');
}
?>
然后在insertL argeData.p hp文件中做数据库操 作。也可以用cron job + php的方式实现大数 据量的处理。
如果是在终端运行ph p命令,当终端关闭后 ,刚刚执行的命令也会 被强制关闭,如果你想 让其不受终端关闭的影 响,可以使用nohu p命令实现:
<?php
//Action代码
public function createActi on(){
//....
//将args替换成 要传给insertL argeData.p hp的参数,参数间用 空格间隔
system('nohup php -f insertLarg eData.php ' . ' args ' . '&');
$this->redirect('/');
}
?>
子进程会自动复制父进
PHP多进程编程示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | <?php //..... //需要安装pcnt if(function_e //生成子进程 $pid = pcntl_fork if($pid == -1){ die('could not fork'); }else{ if($pid){ $status = 0; //阻塞父进程,直到 pcntl_wait // parent proc code exit; }else{ // child proc code //结束当前子进程, if(function_e posix_kill }else{ system('kill -9'. getmypid()); } exit; } } }else{ // 不支持多进程处理时的 } //..... ?> |
如果不需要阻塞进程,
<?php
pcntl_wait
//或
pcntl_wait
在上面的代码中,如果
僵 尸进程是指的父进程已
预防僵尸进程有以下几
- 父 进程通过wait和w
aitpid等函数使 其等待子进程结束,然 后再执行父进程中的代 码,这会导致父进程挂 起。上面的代码就是使 用这种方式实现的,但 在WEB环境下,它不适 合子进程需要长时间运 行的情况(会导致超时 )。使用wait和w aitpid方法使父 进程自动回收其僵尸子 进程(根据子进程的返 回状 态),waitpid 用于临控指定子进程, wait是对于所有子 进程而言。
- 如果父进程很忙,那么
可以用signal函 数为SIGCHLD安 装handler,因 为子进程结束后,父进 程会收到该信号,可以 在handler中调 用wait回收 - 如果父进程不关心子进
程什么时候结束,那么 可以用signal( SIGCHLD, SIG_IGN)通知 内核,自己对子进程的 结束不感兴趣,那么子 进程结束后,内核会回 收,并不再给父进程发 送信号,例如:pcn tl_signal(SIGCHLD, SIG_IGN);
$pid = pcntl_fork();
//....code
- 还有一个技巧,就是f
ork两次,父进程f ork一个子进程,然 后继续工作,子进程再 fork一个孙进程后 退出,那么孙进程被i nit接管,孙进程结 束后,init会回收 。不过子进程的回收还 要自己做。下面是一个 例子: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35#include "apue.h"
#include
int main(void){
pid_t pid;
if ((pid = fork()) < 0){
err_sys("fork error");
} else if (pid == 0){ /**//* first child */
if ((pid = fork()) < 0){
err_sys("fork error");
}elseif(pid > 0){
exit(0); /**//* parent from second fork == first child */
}
/**
* We're the second child; our parent becomes init as soon
* as our real parent calls exit() in the statement above.
* Here's where we'd continue executing,knowing that when
* we're done, init will reap our status.
*/
sleep(2);
printf("second child, parent pid = %d ", getppid());
exit(0);
}
if (waitpid(pid, NULL, 0) != pid) /**//* wait for first child */
err_sys("waitpid error");
/**
* We're the parent (the original process); we continue executing,
* knowing that we're not the parent of the second child.
*/
exit(0);
}
在fork()/execve()过程中,假 设子进程结束时父进程 仍存在,而父进程fo rk()之前既没安装 SIGCHLD信号处 理函数调用waitpid()等 待子进程结束,又没有 显式忽略该信号,则子 进程成为僵尸进程,无 法正常结束,此时即使 是root身份kil l-9也不能杀死僵尸 进 程。补救办法是杀死僵 尸进程的父进程(僵尸 进程的父进程必然存在 ),僵尸进程成为”孤 儿进程”,过继给1号 进程init,ini t会定期调用wait 回收 清理这些父进程已退出 的僵尸子进程。
所以,上面的示例可以改成:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49<?php
//.....
//需要安装pcnt
if(function_e
//生成第一个子进程
$pid = pcntl_fork
if($pid == -1){
//子进程fork失
die('could not fork');
}else{
if($pid){
//父进程code
sleep(5); //等待5秒
exit(0); //或$this->
}else{
//第一个子进程co
//产生孙进程
if(($gpid = pcntl_fork
//孙进程产生失败
die('could not fork');
}elseif($gpid > 0){
//第一个子进程co
$status = 0;
$status = pcntl_wait
if($status ! = 0) file_put_c
//得到父进程id
//$ppid = posix_getp
//得到子进程id:
//kill掉子进程
//posix_ki
exit(0);
}else{ //即$gpid == 0
//孙进程code
//....
//结束孙进程(即当
if(function_e
posix_kill
}else{
system('kill -9'. getmypid());
}
exit(0);
}
}
}
}else{
// 不支持多进程处理时的
}
//.....
?>
怎样产生僵尸进程的
一 个进程在调用exit
任何一个子进程(in
如果父进程在子进程结
另外,还可以写一个p
<?php
//Action代码
public function createActi
//....
//将args替换成
system('php -f insertLarg
$this->redirect('/');
}
?>
然后在insertL
如果是在终端运行ph
<?php
//Action代码
public function createActi
//....
//将args替换成
system('nohup php -f insertLarg
$this->redirect('/');
}
?>