您的位置:首页 > 编程语言 > PHP开发

php多进程编程二

2013-08-02 11:18 309 查看
php在pcntl_fork()后生成的子进程(通常为僵尸进程)必须由pcntl_waitpid()函数进行资源释放。但在pcntl_waitpid()不一定释放的就是当前运行的进程,也可能是过去生成的僵尸进程(没有释放);也可能是并发时其它访问者的僵尸进程。但可以 使用posix_kill($cid, SIGTERM)在子进程结束时杀掉它。

子进程会自动复制父进程空间里的变量。
PHP多进程编程示例2
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
//.....
//需要安装pcntl的php扩展,并加载它
if(function_exists("pcntl_fork")){

   //生成子进程

  $pid = pcntl_fork();

  if($pid
== -1){

    die('could not fork');

  }else{

    if($pid){

      $status =
0;

      //阻塞父进程,直到子进程结束,不适合需要长时间运行的脚本,可使用pcntl_wait($status, 0)实现非阻塞式

      pcntl_wait($status);

      // parent proc code

      exit;

    }else{

      // child proc code

      //结束当前子进程,以防止生成僵尸进程

      if(function_exists("posix_kill")){

        posix_kill(getmypid(), SIGTERM);

      }else{

        system('kill -9'.
getmypid());

      }

      exit;

    }

  }
}else{

   // 不支持多进程处理时的代码在这里
}
//.....
[b]?>
[/b]
如果不需要阻塞进程,而又想得到子进程的退出状态,则可以注释掉pcntl_wait($status)语句,或写成:
<?php

pcntl_wait($status,
1);
//或

pcntl_wait($status, WNOHANG);

在上面的代码中,如果父进程退出(使用exit函数退出或redirect),则会导致子进程成为僵尸进程(会交给init进程控制),子进程不再执行。

僵 尸进程是指的父进程已经退出,而该进程dead之后没有进程接受,就成为僵尸进程.(zombie)进程。任何进程在退出前(使用exit退出)都会变成 僵尸进程(用于保存进程的状态等信息),然后由init进程接管。如果不及时回收僵尸进程,那么它在系统中就会占用一个进程表项,如果这种僵尸进程过多, 最后系统就没有可以用的进程表项,于是也无法再运行其它的程序。

预防僵尸进程有以下几种方法:
父 进程通过wait和waitpid等函数使其等待子进程结束,然后再执行父进程中的代码,这会导致父进程挂起。上面的代码就是使用这种方式实现的,但在WEB环境下,它不适合子进程需要长时间运行的情况(会导致超时)。使用wait和waitpid方法使父进程自动回收其僵尸子进程(根据子进程的返回状 态),waitpid用于临控指定子进程,wait是对于所有子进程而言。
如果父进程很忙,那么可以用signal函数为SIGCHLD安装handler,因为子进程结束后,父进程会收到该信号,可以在handler中调用wait回收如果父进程不关心子进程什么时候结束,那么可以用signal(SIGCHLD, SIG_IGN)通知内核,自己对子进程的结束不感兴趣,那么子进程结束后,内核会回收,并不再给父进程发送信号,例如:pcntl_signal(SIGCHLD, SIG_IGN);
$pid = pcntl_fork();
//....code

还有一个技巧,就是fork两次,父进程fork一个子进程,然后继续工作,子进程再fork一个孙进程后退出,那么孙进程被init接管,孙进程结束后,init会回收。不过子进程的回收还要自己做。下面是一个例子:
<
4000
/strong>[b]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()过程中,假设子进程结束时父进程仍存在,而父进程fork()之前既没安装SIGCHLD信号处理函数调用waitpid()等待子进程结束,又没有显式忽略该信号,则子进程成为僵尸进程,无法正常结束,此时即使是root身份kill-9也不能杀死僵尸进 程。补救办法是杀死僵尸进程的父进程(僵尸进程的父进程必然存在),僵尸进程成为”孤儿进程”,过继给1号进程init,init会定期调用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
[b]<?php


//.....
//需要安装pcntl的php扩展,并加载它
if(function_exists("pcntl_fork")){

   //生成第一个子进程

  $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{

      //第一个子进程code

      //产生孙进程

      if(($gpid
= pcntl_fork())
< 0){
////$gpid即所产生的孙进程id

        //孙进程产生失败

        die('could not fork');

      }elseif($gpid
> 0){

        //第一个子进程code,即孙进程的父进程

        $status =
0;

        $status = pcntl_wait($status);
//阻塞子进程,并返回孙进程的退出状态,用于检查是否正常退出

        if($status
! =
0) file_put_content('filename',
'孙进程异常退出');

        //得到父进程id

        //$ppid =  posix_getppid(); //如果$ppid为1则表示其父进程已变为init进程,原父进程已退出

        //得到子进程id:posix_getpid()或getmypid()或是fork返回的变量$pid

        //kill掉子进程

        //posix_kill(getmypid(), SIGTERM);

        exit(0);

      }else{
//即$gpid == 0

        //孙进程code

        //....

        //结束孙进程(即当前进程),以防止生成僵尸进程

        if(function_exists('posix_kill')){

           posix_kill(getmypid(), SIGTERM);

        }else{

           system('kill -9'.
getmypid());

        }

        exit(0);

      }

    }

  }
}else{

   // 不支持多进程处理时的代码在这里
}
//.....
?>

怎样产生僵尸进程的[/b]

一 个进程在调用exit命令结束自己的生命的时候,其实它并没有真正的被销毁,而是留下一个称为僵尸进程(Zombie)的数据结构(系统调用exit,它 的作用是使进程退出,但也仅仅限于将一个正常的进程变成一个僵尸进程,并不能将其完全销毁)。在Linux进程的状态中,僵尸进程是非常特殊的一种,它已 经放弃了几乎所有内存空间,没有任何可执行代码,也不能被调度,仅仅在进程列表中保留一个位置,记载该进程的退出状态等信息供其他进程收集,除此之外,僵
尸进程不再占有任何内存空间。它需要它的父进程来为它收尸,如果他的父进程没安装SIGCHLD信号处理函数调用wait或waitpid()等待子进程 结束,又没有显式忽略该信号,那么它就一直保持僵尸状态,如果这时父进程结束了,那么init进程自动会接手这个子进程,为它收尸,它还是能被清除的。但 是如果如果父进程是一个循环,不会结束,那么子进程就会一直保持僵尸状态,这就是为什么系统中有时会有很多的僵尸进程。

任何一个子进程(init除外)在exit()之后,并非马上就消失掉,而是留下一个称为僵尸进程(Zombie)的数据结构,等待父进程处理。这是每个子进程在结束时都要经过的 阶段。如果子进程在exit()之后,父进程没有来得及处理,这时用ps命令就能看到子进程的状态是”Z”。
9fb3
如果父进程能及时  处理,可能用ps命令就来不及看到子进程的僵尸状态,但这并不等于子进程不经过僵尸状态。

如果父进程在子进程结束之前退出,则子进程将由init接管。init将会以父进程的身份对僵尸状态的子进程进行处理。

另外,还可以写一个php文件,然后在以后台形式来运行它,例如:
<?php

  //Action代码

  [b]public

function createAction(){

    //....

    //将args替换成要传给insertLargeData.php的参数,参数间用空格间隔

    system('php -f insertLargeData.php '
. ' args '
. '&');

    $this->redirect('/');

  }
?>

然后在insertLargeData.php文件中做数据库操作。也可以用cronjob + php的方式实现大数据量的处理。

如果是在终端运行php命令,当终端关闭后,刚刚执行的命令也会被强制关闭,如果你想让其不受终端关闭的影响,可以使用nohup命令实现:
<?php

  //Action代码

  public
function createAction(){

    //....

    //将args替换成要传给insertLargeData.php的参数,参数间用空格间隔

    system('nohup php -f insertLargeData.php '
. ' args '
. '&');

    $this->redirect('/');

  }
?>
[/b]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  php多进程