码迷,mamicode.com
首页 > Web开发 > 详细

php多进程实例

时间:2018-04-25 22:04:02      阅读:332      评论:0      收藏:0      [点我收藏+]

标签:ons   hid   sap   参考   argv   环境   控制   rds   mic   

  在前面的文章《php多进程和多线程的比较》中已经介绍了一些多进程的基础知识,这篇文章呢,主要是结合实例学习一下,php多进程的用途。文章分为三部分,第一部分介绍多进程用到的一些函数;第二部分介绍一个简单的多进程示例,第三部分介绍一个利用php多进程的用途——守护进程。

 多进程相关函数

  1.$pid = pcntl_fork();//创建一个子进程

   pcntl_fork 的返回值是一个int值 。如果$pid=-1 ,fork进程失败 ;如果$pid=0, 当前的上下文环境为worker ; 如果$pid>0 当前的上下文环境为master,这个pid就是fork的worker的pid。

  2.$pid = pcntl_wait($status, WNOHANG)// 等待或返回fork的子进程状态

   pcntl_wait()将会存储状态信息到status 参数上;WNOHANG表示如果没有子进程退出立刻返回。函数的返回值$pid是一个Int值,如果是子进程退出了,表示子进程号;如果发生错误返回-1,如果提供了 WNOHANG作为option,并且没有可用子进程时返回0。主要作用是防止产生僵尸进程。子进程结束时,父进程没有等待它(通过调用wait或者waitpid),那么子进程结束后不会释放所有资源,这就是僵尸进程。
  3.posix_getpid();//返回当前进程的pid
 
  4.posix_getppid();//返回当前进程父进程的pid
 
  5.bool posix_mkfifo ( string $pathname , int $mode )//创建一个管道(用于子进程之间的通讯,可以想象成文件,但是管道的读写都是自动上锁的,原子性操作)
  $pathname-管道存放的路径;$mode-即用户对于该管道的权限,和文件权限umask一样。返回值bool,true代表创建成功。
  6. fopen ( string $filename , string $mode)//打开管道
  这个就是打开文件的操作,参数和操作文件是一致的。
 
  7.fwrite,fclose,fread//写,关闭,读管道,方法和操作文件基本一致
 

 多进程示例

  多进程的一个典型应用就是可以并行的去处理几件事情,比如并行的去请求几个url,并行的去发送邮件等。继续衍生,可以多个进程去消息队列里面去取任务来执行,也可以模拟Web服务器处理http请求的操作等。下面是一个简单的示例:

技术分享图片
 1 <?php
 2     define(‘NUM‘,5);
 3     
 4     //先创建管道,1.pipe是管道名
 5     if(!posix_mkfifo ( "pipefff" , 0666 )){
 6         die("create 1.pipe error");
 7     }
 8     
 9     for($i=0;$i<NUM;$i++){
10         $pid = pcntl_fork();
11         if($pid == -1){
12             die("fork error");
13         }else if($pid == 0){//子进程空间
14             sleep(1);
15             echo "子进程ID:".posix_getpid().PHP_EOL;
16             exit(0);
17         }else{//主进程空间
18             echo "父进程ID:".posix_getpid().PHP_EOL;
19             pcntl_wait($status);
20         }
21     }    
22 
23 unlink("pipefff"); // 删除管道,已经没有作用了    
View Code

示例,简单演示了多进程的创建和管道的创建,以及何为子进程空间。

  一个进一步的例子,用来多进程请求url,可以参考下面的代码,相比单进程去请求,极大的减少了程序的运行时间(也是因为请求url是I/O密集型的任务,如果是cpu密集型的任务在单核下效果不明显):

技术分享图片
 1 <?php
 2 class WebServer
 3 {
 4     private $list;
 5     public function __construct()
 6     {
 7         $this->list = [];
 8     }
 9     public function worker($request){
10         $pid = pcntl_fork();
11         if($pid == -1){
12             return false;
13         }
14         if($pid > 0){
15             return $pid;
16         }
17         if($pid == 0){
18             $time = $request[0];
19             $method = $request[1];
20             $start = microtime(true);
21             echo getmypid()."\t start ".$method."\tat".$start.PHP_EOL;
22             //sleep($time);
23             $c = file_get_contents($method);
24            // echo getmypid() ."\n";
25             $end = microtime(true);
26             $cost = $end-$start;
27             echo getmypid()."\t stop \t".$method."\tat:".$end."\tcost:".$cost.PHP_EOL;
28             exit(0);
29         }
30     }
31     public function master($requests){
32         $start = microtime(true);
33         echo "All request handle start at ".$start.PHP_EOL;
34         foreach ($requests as $request){
35             $pid = $this->worker($request);
36             if(!$pid){
37                 echo ‘handle fail!‘.PHP_EOL;
38                 return;
39             }
40             array_push($this->list,$pid);
41         }
42         while(count($this->list)>0){
43             foreach ($this->list as $k=>$pid){
44                 $res = pcntl_waitpid($pid,$status,WNOHANG);
45                 if($res == -1 || $res > 0){
46                     unset($this->list[$k]);
47                 }
48             }
49             usleep(100);
50         }
51         $end = microtime(true);
52         $cost = $end - $start;
53         echo "All request handle stop at ".$end."\t cost:".$cost.PHP_EOL;
54     }
55 }
56 
57 $requests = [
58   [1,‘http://www.sina.com‘],
59   [2,‘http://www.sina.com‘],
60   [3,‘http://www.sina.com‘],
61   [4,‘http://www.sina.com‘],
62   [5,‘http://www.sina.com‘],
63   [6,‘http://www.sina.com‘]
64 ];
65 
66 echo "多进程测试:".PHP_EOL;
67 $server = new WebServer();
68 $server->master($requests);
69 
70 echo PHP_EOL."单进程测试:".PHP_EOL;
71 $start = microtime(true);
72 for($i=0;$i<6;$i++){
73     $c = file_get_contents("http://www.sina.com");
74 }
75 $end = microtime(true);
76 $cost = $end - $start;
77 echo "All request handle stop at ".$end."\t cost:".$cost.PHP_EOL;
View Code

 

 多进程的用途

  介绍一个多进程的用途,编写守护进程。

守护进程是脱离于终端并且在后台运行的进程。守护进程脱离于终端是为了避免进程在执行过程中的信息在任何终端上显示并且进程也不会被任何终端所产生的终端信息所打断。

  为什么开发守护进程?
  很多程序以服务形式存在,他没有终端或UI交互,它可能采用其他方式与其他程序交互,如TCP/UDP Socket, UNIX Socket, fifo。程序一旦启动便进入后台,直到满足条件他便开始处理任务。
  创建守护进程步骤:
  1)fork一个子进程;
  2)父进程退出(当前子进程会成为init进程的子进程);
  3)子进程调用setsid(),开启一个新会话,成为新的会话组长,并且释放于终端的关联关系;
  4)子进程再fork子进程,父进程退出(可以防止会话组长重新申请打开终端);
  5)关闭打开的文件描述符;
  6)改变当前工作目录,由于子进程会继承父进程的工作目录,修改工作目录以释放对父进程工作目录的占用。  
  7)清除进程umask。
  参考代码:
技术分享图片
 1 <?php
 2     $pid = pcntl_fork();//创建子进程
 3     if($pid < 0){
 4         die("fork fail");
 5     }else if($pid > 0){
 6         exit();//父进程退出
 7     }
 8     //
 9     if(posix_setsid() === -1){
10         die("Could not detach");//获取会话控制权
11     }
12 
13     $pid = pcntl_fork();//继续创建子进程
14     if($pid < 0){
15         die("fork fail");
16     }else if($pid > 0){
17         exit();//父进程退出
18     }
19     echo "可以看到输出".PHP_EOL;
20     
21     //关闭文件描述符
22     @fclose(STDOUT);//关闭标准输出
23     @fclose(STDERR);//关闭标准出错
24     $STDOUT = fopen(‘/dev/null‘, "a");
25     $STDERR = fopen(‘/dev/null‘, "a");    
26     chdir(‘/‘);//改变当前工作目录
27     umask(0);//清除进程权限
28     echo "不可以看到输出";
29 ?>    
View Code

 

  下面再说明一个例子,利用php守护进程记录服务器的cpu使用率,内存使用率信息。采用面向对象的方式来编写。

技术分享图片
  1 <?php
  2 
  3 /*
  4  * 利用php守护进程记录服务器的cpu使用率,内存使用率信息
  5  */
  6 
  7 class Daemon {
  8 
  9     private $_pidFile;
 10     private $_infoDir;
 11     private $_logFile = null;
 12     private $_jobs = array();
 13 
 14     /*
 15      * 构造函数
 16      * $dir  储存log和pid的绝对路径
 17      */
 18 
 19     public function __construct($dir = "/tmp",$openLog = false) {
 20         $this->_checkPcntl();
 21         $this->_setInfoDir($dir);
 22         $this->_pidFile = rtrim($this->_infoDir, "/") . "/" . __CLASS__ . "_pid.log";
 23         if($openLog == true){
 24             $this->_logFile = rtrim($this->_infoDir, "/") . "/" . __CLASS__ . "_pid_log.log"; 
 25             file_put_contents($this->_logFile, "",FILE_APPEND);
 26         }
 27     }
 28 
 29     private function _checkPcntl() {//检查是否开启pcntl模块
 30         !function_exists(‘pcntl_signal‘) && die(‘Error:Need PHP Pcntl extension!‘);
 31     }
 32 
 33     private function _setInfoDir($dir = null) {//设置信息储存的目录
 34         if (is_dir($dir)) {
 35             $this->_infoDir = $dir;
 36         } else {
 37             $this->_infoDir = __DIR__;
 38         }
 39     }
 40 
 41     private function _getPid() {//获取pid
 42         if (!file_exists($this->_pidFile)) {
 43             return 0;
 44         }
 45         $pid = intval(file_get_contents($this->_pidFile));
 46         if (posix_kill($pid, SIG_DFL)) {//给进程发一个信号
 47             return $pid;
 48         } else {
 49             unlink($this->_pidFile);
 50             return 0;
 51         }
 52     }
 53 
 54     private function _message($message) {//输出相关信息
 55         printf("%s %d %d %s" . PHP_EOL, date("Y-m-d H:i:s"), posix_getppid(), posix_getpid(), $message);
 56         if ($this->_logFile != null) {
 57             file_put_contents($this->_logFile, date("Y-m-d H:i:s") . " " . posix_getppid() . " " . posix_getpid() . " " . $message . PHP_EOL, FILE_APPEND);
 58         }
 59     }
 60 
 61     private function daemonize() {//创建守护程序
 62         if (!preg_match("/cli/i", php_sapi_name())) {
 63             $this->_message("‘Should run in CLI");
 64             die();
 65         }
 66         $pid = pcntl_fork();
 67         if ($pid < 0) {
 68             $this->_message("Fork fail");
 69             die();
 70         } elseif ($pid > 0) {
 71             exit(0);
 72         }
 73         
 74         if (posix_setsid() === -1) {
 75             $this->_message("Could not detach");
 76             die(); //获取会话控制权
 77         }
 78 //        $pid = pcntl_fork();
 79 //        if ($pid < 0) {
 80 //            $this->_message("Fork fail");
 81 //            die();
 82 //        } elseif ($pid > 0) {
 83 //            exit(0);
 84 //        }
 85 //        fclose(STDIN);
 86 //        fclose(STDOUT);
 87 //        fclose(STDERR);
 88         chdir("/");
 89         umask(0);
 90 
 91         $fp = fopen($this->_pidFile, ‘w‘) or die("Can‘t create pid file");
 92         fwrite($fp, posix_getpid());
 93         fclose($fp);
 94 
 95         //进入守护进程,处理任务
 96         if (!empty($this->_jobs)) {
 97             foreach ($this->_jobs as $job) {
 98                 call_user_func($job["function"], $job["argv"]);
 99             }
100         }
101         //file_put_contents("/root/wangli/test/daemon/Daemon_pid_log.log", "qqqqqqqqqqq");
102         return;
103     }
104 
105     private function start() {//程序启动
106         //1.启动前判断是否已经存在一个进程
107         //2.没有则创建一个守护进程
108         if ($this->_getPid() > 0) {
109             $this->_message("Is Running");
110             exit();
111         }
112         $this->daemonize();
113         $this->_message(‘Start‘);
114     }
115 
116     private function stop() {//停止守护进程
117         $pid = $this->_getPid();
118         if ($pid > 0) {
119             posix_kill($pid, SIGTERM);
120             unlink($this->_pidFile);
121             $this->_message("Stopped");
122         } else {
123             $this->_message("Not Running");
124         }
125     }
126     
127     private function status() {
128             if ($this->_getPid() > 0) {
129                 $this->_message(‘Is Running‘);
130             } else {
131                 $this->_message(‘Not Running‘);
132             }
133     }    
134 
135     public function main($argv) {//程序控制台
136         $param = is_array($argv) && count($argv) == 2 ? $argv[1] : null;
137         switch ($param) {
138             case ‘start‘ :
139                 $this->start();
140                 break;
141             case ‘stop‘:
142                 $this->stop();
143                 break;
144             case ‘status‘:
145                 $this->status();
146                 break;
147             default :
148                 echo "Argv start|stop|status" . PHP_EOL;
149         }
150     }
151 
152     public function addJobs($jobs) {
153         if (!isset($jobs[‘function‘]) || empty($jobs[‘function‘])) {
154             $this->_message(‘Need function param‘);
155         }
156 
157         if (!isset($jobs[‘argv‘]) || empty($jobs[‘argv‘])) {
158             $jobs[‘argv‘] = "";
159         }
160         $this->_jobs[] = $jobs;
161     }
162 
163 }
164 
165 $daemon = new Daemon(__FILE__,true);
166 $daemon->addJobs(array(
167     ‘function‘ => ‘recordServerData‘,
168     ‘argv‘ => ‘GOGOGO‘
169 ));
170 
171 $daemon->main($argv);
172 
173 
174 function recordServerData($param) {
175     $i = 0;
176     while (true) {
177             $status=get_used_status(); //获取服务器情况的函数
178             file_put_contents(‘/root/wangli/test/daemon/server.log‘, $status["detection_time"]." cpu_usage:".$status["cpu_usage"].PHP_EOL, FILE_APPEND);
179             sleep(5);
180     }
181 }
182 
183 
184 function get_used_status() {
185     $fp = popen(‘top -b -n 2 | grep -E "^(Cpu|Mem|Tasks)"‘, "r"); //获取某一时刻系统cpu和内存使用情况
186     $rs = "";
187     while (!feof($fp)) {
188         $rs .= fread($fp, 1024);
189     }
190     pclose($fp);
191     $sys_info = explode("\n", $rs);
192 
193     $tast_info = explode(",", $sys_info[3]); //进程 数组
194     $cpu_info = explode(",", $sys_info[4]);  //CPU占有量  数组
195     $mem_info = explode(",", $sys_info[5]); //内存占有量 数组
196     //正在运行的进程数
197     $tast_running = trim(trim($tast_info[1], ‘running‘));
198 
199 
200     //CPU占有量
201     $cpu_usage = trim(trim($cpu_info[0], ‘Cpu(s): ‘), ‘%us‘);  //百分比
202     //内存占有量
203     $mem_total = trim(trim($mem_info[0], ‘Mem: ‘), ‘k total‘);
204     $mem_used = trim($mem_info[1], ‘k used‘);
205     $mem_usage = round(100 * intval($mem_used) / intval($mem_total), 2);  //百分比
206 
207 
208 
209     $fp = popen(‘df -lh | grep -E "^(/)"‘, "r");
210     $rs = fread($fp, 1024);
211     pclose($fp);
212     $rs = preg_replace("/\s{2,}/", ‘ ‘, $rs);  //把多个空格换成 “_”
213     $hd = explode(" ", $rs);
214     $hd_avail = trim($hd[3], ‘G‘); //磁盘可用空间大小 单位G
215     $hd_usage = trim($hd[4], ‘%‘); //挂载点 百分比
216     //print_r($hd);
217     //检测时间
218     $fp = popen(‘date +"%Y-%m-%d %H:%M"‘, "r");
219     $rs = fread($fp, 1024);
220     pclose($fp);
221     $detection_time = trim($rs);
222 
223 
224     return array(‘cpu_usage‘ => $cpu_usage, ‘mem_usage‘ => $mem_usage, ‘hd_avail‘ => $hd_avail, ‘hd_usage‘ => $hd_usage, ‘tast_running‘ => $tast_running, ‘detection_time‘ => $detection_time);
225 }
226 ?>    
View Code

 

    
  
 
 
 
 
 
 
参考文献:

php多进程实例

标签:ons   hid   sap   参考   argv   环境   控制   rds   mic   

原文地址:https://www.cnblogs.com/Andres/p/8934136.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!