分享web开发知识

注册/登录|最近发布|今日推荐

主页 IT知识网页技术软件开发前端开发代码编程运营维护技术分享教程案例
当前位置:首页 > 技术分享

php多进程实例

发布时间:2023-09-06 01:51责任编辑:傅花花关键词:暂无标签

  在前面的文章《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‘); //磁盘可用空间大小 单位G215 ????$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多进程处理并行处理任务实例

php多进程使用场景

PHP高级编程之守护进程

php多进程实例

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

知识推荐

我的编程学习网——分享web前端后端开发技术知识。 垃圾信息处理邮箱 tousu563@163.com 网站地图
icp备案号 闽ICP备2023006418号-8 不良信息举报平台 互联网安全管理备案 Copyright 2023 www.wodecom.cn All Rights Reserved