[ 起源]Linux/Unix下守護程式(Daemon)大家都知道,例如我們常用的httpd、mysqld等等,就是常駐記憶體運行的程序,類似Windows下的服務。一般守護程序都是使用C/C++來寫,就是透過fork生成子程序,當前台shell下的父進程被殺掉,子程序就轉到後台運行,為了不在終端機產生輸出信息,就通過syslog等函數來寫日誌檔。
我們知道php是腳本語言,透過php的腳本引擎來執行,所以要做成守護程式比較麻煩,我們今天就來結合Unix/Linux的指令來實現我們守護程式的功能。
[ 原理]Unix中的nohup指令的功能就是不掛斷地執行指令,同時nohup把程式的所有輸出到放到目前目錄的nohup.out檔中,如果檔案不可寫,則放到<用戶主目錄> /nohup.out 檔案中。那麼有了這個指令以後,我們的php程式就寫程shell腳本,使用循環來讓我們的腳本一直運行,那麼不管我們終端機視窗是否關閉,都能夠讓我們的php腳本一直運行。當然,當我們的php行程被殺或是我們的作業系統重新啟動了,自然就會中止了。
[ 功能]一定會問,讓我們的php腳本做了守護程式又有什麼用處呢?當然有,例如最典型的作用,能夠基本的替代cron的功能,例如我們需要定期實行的某些操作,完全可以交給它來做,不再需要cron,當然,如果伺服器重啟就沒有辦法了,不過,一般的Unix伺服器可沒那麼容易重新啟動的。另外,我們還可以做一個簡單的伺服器端的功能,例如做一個能夠Telnet過去的伺服器,嘿嘿,可以做成一個小後門,不過這樣實作稍微有點複雜。
[ 實踐]例子一:自動產生文件我們現在來做兩個例子來證明我們上面的說法。首先第一個是每個三十秒自動產生一個文件,永遠執行下去。
首必須確保作業系統是Unix或Linux,例如可以是FreeBSD、Redhat、Fedora或SUSE什麼的。然後我們必須確保我們的php腳本引擎是在/usr/local/php/bin/php,具體路徑可以按照你實際路徑來寫,如果沒有腳本引擎,請自行安裝。
例如目前目錄是/home/heiyeluren/,那我們用vi或其他編輯器來寫一個叫做php_daemon1.php的檔案:
$ vi php_daemon1.php
然後寫入如下程式碼:
#! /usr/local/php/bin/php
<?
set_time_limit(0);
while(1)
{
@fopen(”test_”.time().”.txt”,”w”);
sleep(30);
}
?>
然後儲存並且退出vi,然後賦予php_daemon1.php檔案可執行權限:
$ chmod +x /home/heiyeluren/php_daemon1.php
然後再讓我們的腳本再後台執行,執行以下指令:
$ nohup /home/heiyeluren/php_daemon1.php &
記得最後加上& 符號,這樣才能夠跑到後台去運行,執行上述指令後出現如下提示:
[1] 82480
appending output to nohup.out
再回後車後將出現shell提示符號。那麼上面的提示就是說,所有指令執行的輸出訊息都會放到nohup.out檔中,這個上面已經講了。然後執行上面指令後,我們每個三十秒在目前目錄就會看到多出以test_開頭的文件,例如:test_1139901144.txt test_1139901154.txt等等文件,那麼就證明我們的程式已經再後台執行了。
那我們要如何終止程式的運作呢?最好方法就是重啟作業系統,呵呵,當然,這是不可取的,我們可以使用kill指令來殺掉這個進程,殺進程之前自然後知道進程的PID號,就是Process ID,使用ps指令就能夠看到了。
$ ps
PID TT STAT TIME COMMAND
82374 p3 Ss 0:00.14 -bash (bash)
82510 p3 S 0:00.06 /usr/local/php/bin/php /home/heiyeluren/php_daemon1.php
82528 p3 R+ 0:00.00 ps
上面我們已經看到了我們的php的進程id是:82510 ,於是我們再執行kill指令:
$ kill -9 82510
[1]+ Killed nohup /home/heiyeluren/php_daemon1.php
看到這麼提示就明白這個過程被殺了,再ps,就會發現沒有了:
$ ps
PID TT STAT TIME COMMAND
82374 p3 Ss 0:00.17 -bash (bash)
82535 p3 R+ 0:00.00 ps
如果直接ps指令無法看到進程,那麼就使用ps & apos 兩個結合指令來查看,一定能夠看到進程。
再上面的基礎上進程擴展,能夠做成屬於自己的cron程序,那就不需要cron啦,當然,這只是一種方式例子二:伺服器端的守護程序這個例子跟網絡有關,大致就是模擬使用php做伺服器端,然後一直後台運行,達到伺服器端Daemon的效果。
繼續在我們的主目錄下:/home/heiyeluren,編輯檔案php_daemon2.php:
$ vi php_daemon2.php
輸入如下程式碼(程式碼來自PHP手冊,我進行了修改註解):
#! /usr/local/php/bin/php
<?php
/* http://www.knowsky.com/php.asp設定不顯示任何錯誤*/
error_reporting(0);
/* 腳本超時為無限*/
set_time_limit(0);
/* 開始固定清除*/
ob_implicit_flush();
/* 本機的IP和需要開放的連接埠*/
$address = '192.168.0.1';
$port = 10000;
/* 產生一個Socket */
if (($sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) < 0) {
echo “socket_create() failed: reason: ” . socket_strerror($sock) . “n”;
}
/* 把IP位址連接埠進行綁定*/
if (($ret = socket_bind($sock, $address, $port)) < 0) {
echo “socket_bind() failed: reason: ” . socket_strerror($ret) . “n”;
}
/* 監聽Socket連線*/
if (($ret = socket_listen($sock, 5)) < 0) {
echo “socket_listen() failed: reason: ” . socket_strerror($ret) . “n”;
}
/* 永遠循環監接受使用者連線*/
do {
if (($msgsock = socket_accept($sock)) < 0) {
echo “socket_accept() failed: reason: ” . socket_strerror($msgsock) . “n”;
break;
}
/* 發送提示訊息給連接上來的使用者*/
$msg = “==========================================rn ” .
” Welcome to the PHP Test Server. rnrn”.
” To quit, type 'quit'. rn” .
” To shut down the server type 'shutdown'.rn” .
” To get help message type 'help'.rn” .
”============================================rn” .
”php> “;
socket_write($msgsock, $msg, strlen($msg));
do {
if (false === ($buf = socket_read($msgsock, 2048, PHP_NORMAL_READ))) {
echo “socket_read() failed: reason: ” . socket_strerror($ret) . “n”;
break 2;
}
if (!$buf = trim($buf)) {
continue;
}
/* 客戶端輸入quit指令時候關閉客戶端連線*/
if ($buf == 'quit') {
break;
}
/* 客戶端輸入shutdown指令時候服務端與客戶端都關閉*/
if ($buf == 'shutdown') {
socket_close($msgsock);
break 2;
}
/* 用戶端輸入help指令時候輸出幫助資訊*/
if ($buf == 'help') {
$msg = ” PHP Server Help Message rnrn”.
” To quit, type 'quit'. rn” .
” To shut down the server type 'shutdown'.rn” .
” To get help message type 'help'.rn” .
”php> “;
socket_write($msgsock, $msg, strlen($msg));
continue;
}
/* 當客戶端輸入指令不存在時提示訊息*/
$talkback = “PHP: unknow command '$buf'.rnphp> “;
socket_write($msgsock, $talkback, strlen($talkback));
echo “$bufn”;
} while (true);
socket_close($msgsock);
} while (true);
/* 關閉Socket連線*/
socket_close($sock);
?>
儲存以上代碼退出。
上面的程式碼大致上就是完成一個類似Telnet伺服器端的功能,就是當伺服器端運行該程式的時候,客戶端能夠連接該伺服器的10000連接埠進行通訊。
加上檔案的可執行權限:
$ chmod +x /home/heiyeluren/php_daemon2.php
在伺服器上執行命令:
$ nohup /home/heiyeluren/php_daemon2.php &
就進入了後台運行,我們透過Windows的客戶端telnet上去:
C:>telnet 192.168.0.1 10000
如果提示:
正在連接到192.168.0.188…無法開啟到主機的連接, 在連接埠10000: 連接失敗則表示伺服器端沒有開啟,或是上面的程式沒有正確執行,請檢查php是否–enable-sockets 功能。如果提示:
==========================================
Welcome to the PHP Test Server.
To quit, type 'quit'.
To shut down the server type 'shutdown'.
To get help message type 'help'.
==========================================
php>
則表示順利連接上了我們的PHP寫的伺服器端守護進程,在php>提示符後面能夠執行help、quit、shutdown等三個命令,如果命令輸入不是這三個,則提示:
php> asdf
PHP: unknow command 'asdf'.
執行help指令可以取得幫助php> help
PHP Server Help Message
To quit, type 'quit'.
To shut down the server type 'shutdown'.
To get help message type 'help'.
這個伺服器端就不介紹了,可以自行擴充。
殺進程跟例子一類似。
[ 總結]透過以上學習,我們知道php也可以做守護進程,如果設計的好,功能也會比較強大,不過我們這裡只是學習而已,可以自行研究更新。
本文參考了php中文手冊,多看手冊,對自己非常有好處。