PHP高級編程實(shí)例:編寫守護(hù)進(jìn)程
1.什么是守護(hù)進(jìn)程
守護(hù)進(jìn)程是脫離于終端并且在后臺運(yùn)行的進(jìn)程。守護(hù)進(jìn)程脫離于終端是為了避免進(jìn)程在執(zhí)行過程中的信息在任何終端上顯示并且進(jìn)程也不會被任何終端所產(chǎn)生的終端信息所打斷。
例如 apache, nginx, mysql 都是守護(hù)進(jìn)程
2.為什么開發(fā)守護(hù)進(jìn)程
很多程序以服務(wù)形式存在,他沒有終端或UI交互,它可能采用其他方式與其他程序交互,如TCP/UDP Socket, UNIX Socket, fifo。程序一旦啟動便進(jìn)入后臺,直到滿足條件他便開始處理任務(wù)。
3.何時采用守護(hù)進(jìn)程開發(fā)應(yīng)用程序
以我當(dāng)前的需求為例,我需要運(yùn)行一個程序,然后監(jiān)聽某端口,持續(xù)接受服務(wù)端發(fā)起的數(shù)據(jù),然后對數(shù)據(jù)分析處理,再將結(jié)果寫入到數(shù)據(jù)庫中; 我采用ZeroMQ實(shí)現(xiàn)數(shù)據(jù)收發(fā)。
如果我不采用守護(hù)進(jìn)程方式開發(fā)該程序,程序一旦運(yùn)行就會占用當(dāng)前終端窗框,還有受到當(dāng)前終端鍵盤輸入影響,有可能程序誤退出。
4.守護(hù)進(jìn)程的安全問題
我們希望程序在非超級用戶運(yùn)行,這樣一旦由于程序出現(xiàn)漏洞被駭客控制,攻擊者只能繼承運(yùn)行權(quán)限,而無法獲得超級用戶權(quán)限。
我們希望程序只能運(yùn)行一個實(shí)例,不運(yùn)行同事開啟兩個以上的程序,因?yàn)闀霈F(xiàn)端口沖突等等問題。
5.怎樣開發(fā)守護(hù)進(jìn)程
例 1. 守護(hù)進(jìn)程例示
<?php class ExampleWorker extends Worker { #public function __construct(Logging $logger) { # $this->logger = $logger; #} #protected $logger; protected static $dbh; public function __construct() { } public function run(){ $dbhost = '192.168.2.1'; // 數(shù)據(jù)庫服務(wù)器 $dbport = 3306; $dbuser = 'www'; // 數(shù)據(jù)庫用戶名 $dbpass = 'qwer123'; // 數(shù)據(jù)庫密碼 $dbname = 'example'; // 數(shù)據(jù)庫名 self::$dbh = new PDO("mysql:host=$dbhost;port=$dbport;dbname=$dbname", $dbuser, $dbpass, array( /* PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\'', */ PDO::MYSQL_ATTR_COMPRESS => true, PDO::ATTR_PERSISTENT => true ) ); } protected function getInstance(){ return self::$dbh; } } /* the collectable class implements machinery for Pool::collect */ class Fee extends Stackable { public function __construct($msg) { $trades = explode(",", $msg); $this->data = $trades; print_r($trades); } public function run() { #$this->worker->logger->log("%s executing in Thread #%lu", __CLASS__, $this->worker->getThreadId() ); try { $dbh = $this->worker->getInstance(); $insert = "INSERT INTO fee(ticket, login, volume, `status`) VALUES(:ticket, :login, :volume,'N')"; $sth = $dbh->prepare($insert); $sth->bindValue(':ticket', $this->data[0]); $sth->bindValue(':login', $this->data[1]); $sth->bindValue(':volume', $this->data[2]); $sth->execute(); $sth = null; /* ...... */ $update = "UPDATE fee SET `status` = 'Y' WHERE ticket = :ticket and `status` = 'N'"; $sth = $dbh->prepare($update); $sth->bindValue(':ticket', $this->data[0]); $sth->execute(); //echo $sth->queryString; //$dbh = null; } catch(PDOException $e) { $error = sprintf("%s,%s\n", $mobile, $id ); file_put_contents("mobile_error.log", $error, FILE_APPEND); } } } class Example { /* config */ const LISTEN = "tcp://192.168.2.15:5555"; const MAXCONN = 100; const pidfile = __CLASS__; const uid = 80; const gid = 80; protected $pool = NULL; protected $zmq = NULL; public function __construct() { $this->pidfile = '/var/run/'.self::pidfile.'.pid'; } private function daemon(){ if (file_exists($this->pidfile)) { echo "The file $this->pidfile exists.\n"; exit(); } $pid = pcntl_fork(); if ($pid == -1) { die('could not fork'); } else if ($pid) { // we are the parent //pcntl_wait($status); //Protect against Zombie children exit($pid); } else { // we are the child file_put_contents($this->pidfile, getmypid()); posix_setuid(self::uid); posix_setgid(self::gid); return(getmypid()); } } private function start(){ $pid = $this->daemon(); $this->pool = new Pool(self::MAXCONN, \ExampleWorker::class, []); $this->zmq = new ZMQSocket(new ZMQContext(), ZMQ::SOCKET_REP); $this->zmq->bind(self::LISTEN); /* Loop receiving and echoing back */ while ($message = $this->zmq->recv()) { //print_r($message); //if($trades){ $this->pool->submit(new Fee($message)); $this->zmq->send('TRUE'); //}else{ // $this->zmq->send('FALSE'); //} } $pool->shutdown(); } private function stop(){ if (file_exists($this->pidfile)) { $pid = file_get_contents($this->pidfile); posix_kill($pid, 9); unlink($this->pidfile); } } private function help($proc){ printf("%s start | stop | help \n", $proc); } public function main($argv){ if(count($argv) < 2){ printf("please input help parameter\n"); exit(); } if($argv[1] === 'stop'){ $this->stop(); }else if($argv[1] === 'start'){ $this->start(); }else{ $this->help($argv[0]); } } } $cgse = new Example(); $cgse->main($argv);
5.1. 程序啟動
下面是程序啟動后進(jìn)入后臺的代碼
通過進(jìn)程ID文件來判斷,當(dāng)前進(jìn)程狀態(tài),如果進(jìn)程ID文件存在表示程序在運(yùn)行中,通過代碼file_exists($this->pidfile)實(shí)現(xiàn),但而后進(jìn)程被kill需要手工刪除該文件才能運(yùn)行
private function daemon(){ if (file_exists($this->pidfile)) { echo "The file $this->pidfile exists.\n"; exit(); } $pid = pcntl_fork(); if ($pid == -1) { die('could not fork'); } else if ($pid) { // we are the parent //pcntl_wait($status); //Protect against Zombie children exit($pid); } else { // we are the child file_put_contents($this->pidfile, getmypid()); posix_setuid(self::uid); posix_setgid(self::gid); return(getmypid()); } }
程序啟動后,父進(jìn)程會推出,子進(jìn)程會在后臺運(yùn)行,子進(jìn)程權(quán)限從root切換到指定用戶,同時將pid寫入進(jìn)程ID文件。
5.2. 程序停止
程序停止,只需讀取pid文件,然后調(diào)用posix_kill($pid, 9); 最后將該文件刪除。
private function stop(){ if (file_exists($this->pidfile)) { $pid = file_get_contents($this->pidfile); posix_kill($pid, 9); unlink($this->pidfile); } }
- PHP擴(kuò)展程序?qū)崿F(xiàn)守護(hù)進(jìn)程
- PHP將進(jìn)程作為守護(hù)進(jìn)程的方法
- PHP守護(hù)進(jìn)程實(shí)例
- shell腳本作為保證PHP腳本不掛掉的守護(hù)進(jìn)程實(shí)例分享
- PHP實(shí)現(xiàn)多進(jìn)程并行操作的詳解(可做守護(hù)進(jìn)程)
- PHP程序級守護(hù)進(jìn)程的實(shí)現(xiàn)與優(yōu)化的使用概述
- php守護(hù)進(jìn)程 加linux命令nohup實(shí)現(xiàn)任務(wù)每秒執(zhí)行一次
- 詳解PHP解決守護(hù)進(jìn)程Redis假死
相關(guān)文章
php array_intersect()函數(shù)使用代碼
array_intersect() 返回一個數(shù)組,該數(shù)組包含了所有在 array1 中也同時出現(xiàn)在所有其它參數(shù)數(shù)組中的值。注意鍵名保留不變。2009-01-01php腳本守護(hù)進(jìn)程原理與實(shí)現(xiàn)方法詳解
這篇文章主要介紹了php腳本守護(hù)進(jìn)程原理與實(shí)現(xiàn)方法,較為詳細(xì)的分析了php腳本守護(hù)進(jìn)程的實(shí)現(xiàn)思路、原理、格式及具體實(shí)現(xiàn)方法,需要的朋友可以參考下2017-07-07Erlang的運(yùn)算符(比較運(yùn)算符,數(shù)值運(yùn)算符,移位運(yùn)算符,邏輯運(yùn)算符)
如果要比較兩個數(shù),如果兩個數(shù)之間是不同的類型,比如float和int那么,==操作會首先把兩個數(shù)字轉(zhuǎn)換成相同的相同類型2012-07-07PHP+MySQL高并發(fā)加鎖事務(wù)處理問題解決方法
這篇文章主要介紹了PHP+MySQL高并發(fā)加鎖事務(wù)處理問題解決方法,結(jié)合實(shí)例形式分析了PHP+MySQL事務(wù)處理相關(guān)操作技巧與注意事項(xiàng),需要的朋友可以參考下2018-04-04WordPress的文章自動添加關(guān)鍵詞及關(guān)鍵詞的SEO優(yōu)化
這篇文章主要介紹了給WordPress的文章添加關(guān)鍵詞及關(guān)鍵詞的SEO優(yōu)化方法,突出關(guān)鍵詞在搜尋結(jié)果中的作用,需要的朋友可以參考下2016-03-03PHP常用函數(shù)之根據(jù)生日計(jì)算年齡功能示例
這篇文章主要介紹了PHP常用函數(shù)之根據(jù)生日計(jì)算年齡功能,結(jié)合實(shí)例形式分析了php日期相關(guān)轉(zhuǎn)換與計(jì)算操作技巧,需要的朋友可以參考下2019-10-10windows下升級PHP到5.3.3的過程及注意事項(xiàng)
自從讓自己專注于LAMP方面以來,就一直在關(guān)注PHP社區(qū)的動向,今天上了官網(wǎng)php.net,發(fā)現(xiàn)發(fā)布了新版的了.PHP5.3.3,提高了穩(wěn)定性和安全性,就有了升級的念頭了.2010-10-10