PHP是用C編寫的,因此它對系統底層API的操作與C很像,同大多數語言一樣,PHP程式間通訊的方式有以下幾種:訊息佇列,管道,共享記憶體,socket和訊號。本文是對這幾種通訊方式對整理:
管道通訊PIPE
管道用於承載簡稱之間的通訊資料。為了方便理解,可以將管道比作檔案,程式A將資料寫到管道P中,然後程式B從管道P中讀取資料。php提供的管道操作API與操作檔案的API基本一樣,除了建立管道使用posix_mkfifo函式,讀寫等操作均與檔案操作函式相同。當然,你可以直接使用檔案模擬管道,但是那樣無法使用管道的特性了。
通過管道通訊的大概思路是,首先建立一個管道,然後子程式向管道中寫入資訊,父程式從管道中讀取資訊,這樣就可以做到父子程式直接實現通訊了。
<?php
// 建立管道
$pipePath = "pipe";
if( !file_exists( $pipePath ) ){
if( !posix_mkfifo( $pipePath, 0666) ){
exit(`make pipe false!` . PHP_EOL);
}
}
// 建立程式,子程式寫管道,父程式讀管道
// 通過 pcntl_fork函式建立一個子程式。
// pcntl_fork 函式 很特殊,它呼叫一次擁有 多個返回值。
// 在父程式中:它返回 子程式的ID 這個值是 大於0 的。
// 在子程式中,它返回0。當返回 -1 時表示建立程式失敗。
$pid = pcntl_fork();
if( $pid == 0 ){
// 子程式寫管道
$file = fopen( $pipePath, `w`);
fwrite( $file, `hello world`);
sleep(1);
exit;
}else{
// 父程式讀管道
$file = fopen( $pipePath, `r`);
// 設定成讀取非阻塞
// 當讀取是非阻塞的情況下,父程式進行讀取資訊的時候不會等待,
// 管道中沒有訊息也會立馬返回。
// stream_set_blocking( $file, False);
echo fread( $file, 20) . PHP_EOL;
pcntl_wait($status); // 回收子程式
}
訊息佇列
訊息佇列是存放在記憶體中的一種佇列資料結構。
<?php
// 獲取父程式id
$parentPid = posix_getpid();
echo "parent progress pid:{$parentPid}
";
$childList = array();
// 建立訊息佇列,定義訊息型別
$id = ftok(__FILE__, `m`);
$msgQueue = msg_get_queue($id);
const MSG_TYEP = 1;
// 生產者
function producer()
{
global $msgQueue;
$pid = posix_getpid();
$repeatNum = 5;
for ($i = 0; $i <= $repeatNum; $i++) {
$str = "({$pid}) progress create! {$i}";
msg_send($msgQueue, MSG_TYEP, $str);
$rand = rand(1, 3);
sleep($rand);
}
}
// 消費者
function consumer()
{
global $msgQueue;
$pid = posix_getpid();
$repeatNum = 6;
for ($i = 1; $i<= $repeatNum; $i++) {
$rel = msg_receive($msgQueue, MSG_TYEP, $msgType, 1024, $message);
echo "{$message} | consumer({$pid}) destroy
";
$rand = rand(1, 3);
sleep($rand);
}
}
function createProgress($callback)
{
$pid = pcntl_fork();
if ($pid == -1) {
// 建立失敗
exit("fork progresses error
");
} elseif ($pid == 0) {
// 子程式執行程式
$pid = posix_getpid();
$callback();
exit("({$pid})child progress end!
");
} else {
// 父程式
return $pid;
}
}
for ($i = 0; $i < 3; $i++) {
$pid = createProgress(`producer`);
$childList[$pid] = 1;
echo "create producer progresses: {$pid}
";
}
for ($i = 0; $i < 2; $i++) {
$pid = createProgress(`consumer`);
$childList[$pid] = 1;
echo "create consumer progresses: {$pid}
";
}
while (!empty($childList)) {
$childPid = pcntl_wait($status);
if ($childPid > 0) {
unset($childList[$childPid]);
}
}
echo "({$parentPid})main progress end!
";
執行結果:
create producer progresses: 21432
create producer progresses: 21433
create producer progresses: 21434
create consumer progresses: 21435
(21426) progress create! 2 | consumer(21435) destroy
(21424) progress create! 1 | consumer(21436) destroy
create consumer progresses: 21436
(21426) progress create! 3 | consumer(21436) destroy
(21426) progress create! 4 | consumer(21435) destroy
(21425) progress create! 3 | consumer(21436) destroy
(21424) progress create! 2 | consumer(21435) destroy
(21426) progress create! 5 | consumer(21435) destroy
(21424) progress create! 3 | consumer(21436) destroy
(21433)child progress end!
(21425) progress create! 4 | consumer(21435) destroy
(21424) progress create! 4 | consumer(21436) destroy
(21434)child progress end!
(21424) progress create! 5 | consumer(21435) destroy
(21425) progress create! 5 | consumer(21436) destroy
(21432)child progress end!
(21435)child progress end!
(21436)child progress end!
(21431)main progress end!
訊號量與共享記憶體
<?php
$parentPid = posix_getpid();
echo "parent progress pid:{$parentPid}
";
// 建立共享記憶體,建立訊號量,定義共享key
// ftok(檔案路徑,資源識別符號) 建立一個IPC通訊所需的id
$shm_id = ftok(__FILE__, `m`);
$shm_id = ftok(__FILE__, `s`);
// shm_attach(id) 建立或者開啟一個共享記憶體
$shareMemory = shm_attach($shm_id);
// 返回一個可使用者訪問系統訊號量的id
$signal = sem_get($shm_id);
const SHARE_KEY = 1;
// 生產者
function producer() {
global $shareMemory;
global $signal;
$pid = posix_getpid();
$repeatNum = 5;
for ($i = 1; $i <= $repeatNum; $i++) {
// 獲得訊號量 - 阻塞程式,直到訊號量被獲取到[lock鎖機制的關鍵]
sem_acquire($signal);
// 檢查某個key是否存在與共享記憶體中
if (shm_has_var($shareMemory, SHARE_KEY)) {
// 獲取共享記憶體中的key的值
$count = shm_get_var($shareMemory, SHARE_KEY);
$count ++;
// 為共享記憶體中的key賦值
shm_put_var($shareMemory, SHARE_KEY, $count);
echo "({$pid}) count: {$count}
";
} else {
// 初始化
shm_put_var($shareMemory, SHARE_KEY, 0);
echo "({$pid}) count: 0
";
}
// 釋放
sem_release($signal);
}
}
function createProgress($callback) {
$pid = pcntl_fork();
if ($pid == -1) {
// 建立失敗
exit("fork progress error!
");
} elseif ($pid == 0) {
// 子程式
$pid = posix_getpid();
$callback();
exit("({$pid}) child progress end!
");
} else {
// 父程式
return $pid;
}
}
// 3個寫程式
for ($i = 0; $i < 3; $i ++) {
$pid = createProgress(`producer`);
$childList[$pid] = 1;
echo "create producer child progress: {$pid}
";
}
// 等待所有子程式
while (!empty($childList)) {
$childPid = pcntl_wait($status);
if ($childPid > 0) {
unset($childList[$childPid]);
}
}
// 釋放共享記憶體與訊號量
shm_remove($shareMemory);
sem_remove($signal);
echo "({$parentPid}) main progress end!
";
執行結果:
使用訊號量來實現共享記憶體的鎖機制
parent progress pid:31720
create producer child progress: 31721
create producer child progress: 31722
(31721) count: 0
(31721) count: 1
(31721) count: 2
(31721) count: 3
(31721) count: 4
(31721) child progress end!
create producer child progress: 31723
(31722) count: 5
(31722) count: 6
(31722) count: 7
(31722) count: 8
(31722) count: 9
(31722) child progress end!
(31723) count: 10
(31723) count: 11
(31723) count: 12
(31723) count: 13
(31723) count: 14
(31723) child progress end!
(31720) main progress end!
無鎖情況
Warning: sem_release(): SysV semaphore 4357894312 (key 0x73048925) is not currently acquired in /Users/easyboom/www/example/訊號量與共享記憶體.php on line 38