首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 网站开发 > PHP >

php中兑现多进程

2012-09-03 
php中实现多进程使用PHP真正的多进程运行模式,适用于数据采集、邮件群发、数据源更新、tcp服务器等环节。PHP有

php中实现多进程

使用PHP真正的多进程运行模式,适用于数据采集、邮件群发、数据源更新、tcp服务器等环节。

PHP有一组进程控制函数(编译时需要 –enable-pcntl与posix扩展),使得php能在*nix系统中实现跟c一样的创建子进程、使用exec函数执行程序、处理信号等功能。 PCNTL使用ticks来作为信号处理机制(signal handle callback mechanism),可以最小程度地降低处理异步事件时的负载。何谓ticks?Tick 是一个在代码段中解释器每执行 N 条低级语句就会发生的事件,这个代码段需要通过declare来指定。

常用的PCNTL函数
1. pcntl_alarm ( int $seconds )
设置一个$seconds秒后发送SIGALRM信号的计数器

2. pcntl_signal ( int $signo , callback $handler [, bool $restart_syscalls ] )
为$signo设置一个处理该信号的回调函数。下面是一个隔5秒发送一个SIGALRM信号,并由signal_handler函数获取,然后打印一个“Caught SIGALRM”的例子:

#include <sys/wait.h> ??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回收清理这些父进程已退出的僵尸子进程。

所以,上面的示例可以改成:

?12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849<?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{ ?// 不支持多进程处理时的代码在这里 } //..... ?>

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

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

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

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

?123456789<?php //Action代码 public function createAction(){ ????//.... ????//将args替换成要传给insertLargeData.php的参数,参数间用空格间隔 ????system('php -f insertLargeData.php ' . ' args ' . '&'); ????$this->redirect('/'); } ?>

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

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

?123456789<?php //Action代码 public function createAction(){ ????//.... ????//将args替换成要传给insertLargeData.php的参数,参数间用空格间隔 ????system('nohup php -f insertLargeData.php ' . ' args ' . '&'); ????$this->redirect('/'); } ?>

你还可以使用screen命令代替nohup命令。

?

(转自 http://www.hdj.me/php-multithread)

热点排行