摘要:擴(kuò)展完成兼容機(jī)通用如獲取進(jìn)程殺死進(jìn)程等。擴(kuò)展實(shí)現(xiàn)方式的進(jìn)程間通信之消息隊(duì)列。四進(jìn)程間通信通常中的進(jìn)程通信方式有消息隊(duì)列信號(hào)量共享內(nèi)存信號(hào)管道。下面這個(gè)例子中,父進(jìn)程等待秒鐘,向子進(jìn)程發(fā)送信號(hào)。子進(jìn)程捕獲信號(hào),掉信號(hào)處理函數(shù)處理。
一、引言
進(jìn)程是一個(gè)具有獨(dú)立功能的程序關(guān)于某個(gè)數(shù)據(jù)集合的一次運(yùn)行活動(dòng)。換句話說(shuō)就是,在系統(tǒng)調(diào)度多個(gè)cpu的時(shí)候,一個(gè)程序的基本單元。進(jìn)程對(duì)于大多數(shù)的語(yǔ)言都不是一個(gè)陌生的概念,作為"世界上最好的語(yǔ)言PHP"當(dāng)然也例外。
二、環(huán)境php中的進(jìn)程是以擴(kuò)展的形式來(lái)完成。通過(guò)這些擴(kuò)展,我們能夠很輕松的完成進(jìn)程的一系列動(dòng)作。
pcntl擴(kuò)展:主要的進(jìn)程擴(kuò)展,完成進(jìn)程創(chuàng)建于等待操作。
posix擴(kuò)展:完成posix兼容機(jī)通用api,如獲取進(jìn)程id,殺死進(jìn)程等。
sysvmsg擴(kuò)展:實(shí)現(xiàn)system v方式的進(jìn)程間通信之消息隊(duì)列。
sysvsem擴(kuò)展:實(shí)現(xiàn)system v方式的信號(hào)量。
sysvshm擴(kuò)展:實(shí)現(xiàn)system v方式的共享內(nèi)存。
sockets擴(kuò)展:實(shí)現(xiàn)socket通信。
這些擴(kuò)展只能在linux/mac中使用,window下是不支持。最后建議php版本為5.5+。
相關(guān)代碼:進(jìn)程相關(guān)代碼
三、簡(jiǎn)單的例子一個(gè)簡(jiǎn)單的PHP多進(jìn)程例子,該例子中,一個(gè)子進(jìn)程,一個(gè)父進(jìn)程。子進(jìn)程輸出5次,退出程序。
$parentPid = posix_getpid(); echo "parent progress pid:{$parentPid} "; $childList = array(); $pid = pcntl_fork(); if ( $pid == -1) { // 創(chuàng)建失敗 exit("fork progress error! "); } else if ($pid == 0) { // 子進(jìn)程執(zhí)行程序 $pid = posix_getpid(); $repeatNum = 5; for ( $i = 1; $i <= $repeatNum; $i++) { echo "({$pid})child progress is running! {$i} "; $rand = rand(1,3); sleep($rand); } exit("({$pid})child progress end! "); } else { // 父進(jìn)程執(zhí)行程序 $childList[$pid] = 1; } // 等待子進(jìn)程結(jié)束 pcntl_wait($status); echo "({$parentPid})main progress end!";
完美,終于創(chuàng)建了一個(gè)子進(jìn)程,一個(gè)父進(jìn)程。完了么?沒(méi)有,各個(gè)進(jìn)程之間相互獨(dú)立的,沒(méi)有任何交集,使用范圍嚴(yán)重受到現(xiàn)在。怎么辦,哪就進(jìn)程間通信(interprogress communication)唄。
四、進(jìn)程間通信(IPC)通常linux中的進(jìn)程通信方式有:消息隊(duì)列、信號(hào)量、共享內(nèi)存、信號(hào)、管道、socket。
1.消息隊(duì)列消息隊(duì)列是存放在內(nèi)存中的一個(gè)隊(duì)列。如下代碼將創(chuàng)建3個(gè)生產(chǎn)者子進(jìn)程,2個(gè)消費(fèi)者子進(jìn)程。這5個(gè)進(jìn)程將通過(guò)消息隊(duì)列通信。
$parentPid = posix_getpid(); echo "parent progress pid:{$parentPid} ";$childList = array(); // 創(chuàng)建消息隊(duì)列,以及定義消息類型(類似于數(shù)據(jù)庫(kù)中的庫(kù)) $id = ftok(__FILE__,"m"); $msgQueue = msg_get_queue($id); const MSG_TYPE = 1; // 生產(chǎn)者 function producer(){ global $msgQueue; $pid = posix_getpid(); $repeatNum = 5; for ( $i = 1; $i <= $repeatNum; $i++) { $str = "({$pid})progress create! {$i}"; msg_send($msgQueue,MSG_TYPE,$str); $rand = rand(1,3); sleep($rand); } } // 消費(fèi)者 function consumer(){ global $msgQueue; $pid = posix_getpid(); $repeatNum = 6; for ( $i = 1; $i <= $repeatNum; $i++) { $rel = msg_receive($msgQueue,MSG_TYPE,$msgType,1024,$message); echo "{$message} | consumer({$pid}) destroy "; $rand = rand(1,3); sleep($rand); } } function createProgress($callback){ $pid = pcntl_fork(); if ( $pid == -1) { // 創(chuàng)建失敗 exit("fork progress error! "); } else if ($pid == 0) { // 子進(jìn)程執(zhí)行程序 $pid = posix_getpid(); $callback(); exit("({$pid})child progress end! "); }else{ // 父進(jìn)程執(zhí)行程序 return $pid; } } // 3個(gè)寫進(jìn)程 for ($i = 0; $i < 3; $i ++ ) { $pid = createProgress("producer"); $childList[$pid] = 1; echo "create producer child progress: {$pid} "; } // 2個(gè)寫進(jìn)程 for ($i = 0; $i < 2; $i ++ ) { $pid = createProgress("consumer"); $childList[$pid] = 1; echo "create consumer child progress: {$pid} "; } // 等待所有子進(jìn)程結(jié)束 while(!empty($childList)){ $childPid = pcntl_wait($status); if ($childPid > 0){ unset($childList[$childPid]); } } echo "({$parentPid})main progress end! ";
由于消息隊(duì)列去數(shù)據(jù)是,只有一個(gè)進(jìn)程能去到,所以不需要額外的鎖或信號(hào)量。
2. 信號(hào)量與共享內(nèi)存信號(hào)量:是系統(tǒng)提供的一種原子操作,一個(gè)信號(hào)量,同時(shí)只有你個(gè)進(jìn)程能操作。一個(gè)進(jìn)程獲得了某個(gè)信號(hào)量,就必須被該進(jìn)程釋放掉。
共享內(nèi)存:是系統(tǒng)在內(nèi)存中開(kāi)辟的一塊公共的內(nèi)存區(qū)域,任何一個(gè)進(jìn)程都可以訪問(wèn),在同一時(shí)刻,可以有多個(gè)進(jìn)程訪問(wèn)該區(qū)域,為了保證數(shù)據(jù)的一致性,需要對(duì)該內(nèi)存區(qū)域加鎖或信號(hào)量。
以下,創(chuàng)建多個(gè)進(jìn)程修改內(nèi)存中的同一個(gè)值。
$parentPid = posix_getpid(); echo "parent progress pid:{$parentPid} "; $childList = array(); // 創(chuàng)建共享內(nèi)存,創(chuàng)建信號(hào)量,定義共享key $shm_id = ftok(__FILE__,"m"); $sem_id = ftok(__FILE__,"s"); $shareMemory = shm_attach($shm_id); $signal = sem_get($sem_id); const SHARE_KEY = 1; // 生產(chǎn)者 function producer(){ global $shareMemory; global $signal; $pid = posix_getpid(); $repeatNum = 5; for ( $i = 1; $i <= $repeatNum; $i++) { // 獲得信號(hào)量 sem_acquire($signal); if (shm_has_var($shareMemory,SHARE_KEY)){ // 有值,加一 $count = shm_get_var($shareMemory,SHARE_KEY); $count ++; shm_put_var($shareMemory,SHARE_KEY,$count); echo "({$pid}) count: {$count} "; }else{ // 無(wú)值,初始化 shm_put_var($shareMemory,SHARE_KEY,0); echo "({$pid}) count: 0 "; } // 用完釋放 sem_release($signal); $rand = rand(1,3); sleep($rand); } } function createProgress($callback){ $pid = pcntl_fork(); if ( $pid == -1) { // 創(chuàng)建失敗 exit("fork progress error! "); } else if ($pid == 0) { // 子進(jìn)程執(zhí)行程序 $pid = posix_getpid(); $callback(); exit("({$pid})child progress end! "); }else{ // 父進(jìn)程執(zhí)行程序 return $pid; } } // 3個(gè)寫進(jìn)程 for ($i = 0; $i < 3; $i ++ ) { $pid = createProgress("producer"); $childList[$pid] = 1; echo "create producer child progress: {$pid} "; } // 等待所有子進(jìn)程結(jié)束 while(!empty($childList)){ $childPid = pcntl_wait($status); if ($childPid > 0){ unset($childList[$childPid]); } } // 釋放共享內(nèi)存與信號(hào)量 shm_remove($shareMemory); sem_remove($signal); echo "({$parentPid})main progress end! ";3.信號(hào)
信號(hào)是一種系統(tǒng)調(diào)用。通常我們用的kill命令就是發(fā)送某個(gè)信號(hào)給某個(gè)進(jìn)程的。具體有哪些信號(hào)可以在liunx/mac中運(yùn)行kill -l查看。下面這個(gè)例子中,父進(jìn)程等待5秒鐘,向子進(jìn)程發(fā)送sigint信號(hào)。子進(jìn)程捕獲信號(hào),掉信號(hào)處理函數(shù)處理。
$parentPid = posix_getpid(); echo "parent progress pid:{$parentPid} "; // 定義一個(gè)信號(hào)處理函數(shù) function sighandler($signo) { $pid = posix_getpid(); echo "{$pid} progress,oh no ,I"m killed! "; exit(1); } $pid = pcntl_fork(); if ( $pid == -1) { // 創(chuàng)建失敗 exit("fork progress error! "); } else if ($pid == 0) { // 子進(jìn)程執(zhí)行程序 // 注冊(cè)信號(hào)處理函數(shù) declare(ticks=10); pcntl_signal(SIGINT, "sighandler"); $pid = posix_getpid(); while(true){ echo "{$pid} child progress is running! "; sleep(1); } exit("({$pid})child progress end! "); }else{ // 父進(jìn)程執(zhí)行程序 $childList[$pid] = 1; // 5秒后,父進(jìn)程向子進(jìn)程發(fā)送sigint信號(hào). sleep(5); posix_kill($pid,SIGINT); sleep(5); } echo "({$parentPid})main progress end! ";4.管道(有名管道)
管道是比較常用的多進(jìn)程通信手段,管道分為無(wú)名管道與有名管道,無(wú)名管道只能用于具有親緣關(guān)系的進(jìn)程間通信,而有名管道可以用于同一主機(jī)上任意進(jìn)程。這里只介紹有名管道。下面的例子,子進(jìn)程寫入數(shù)據(jù),父進(jìn)程讀取數(shù)據(jù)。
// 定義管道路徑,與創(chuàng)建管道 $pipe_path = "/data/test.pipe"; if(!file_exists($pipe_path)){ if(!posix_mkfifo($pipe_path,0664)){ exit("create pipe error!"); } } $pid = pcntl_fork(); if($pid == 0){ // 子進(jìn)程,向管道寫數(shù)據(jù) $file = fopen($pipe_path,"w"); while (true){ fwrite($file,"hello world"); $rand = rand(1,3); sleep($rand); } exit("child end!"); }else{ // 父進(jìn)程,從管道讀數(shù)據(jù) $file = fopen($pipe_path,"r"); while (true){ $rel = fread($file,20); echo "{$rel} "; $rand = rand(1,2); sleep($rand); } }5.socket
socket即我們常說(shuō)的套接字編程。這個(gè)待補(bǔ)充。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/22105.html
摘要:接受不到消息消息隊(duì)列通過(guò)指定而被創(chuàng)建后,任意一方銷毀了該隊(duì)列,都會(huì)導(dǎo)致其他發(fā)送或接收方失敗。用法場(chǎng)景進(jìn)程,中代碼段要用到中代碼段的結(jié)果。完成了進(jìn)程間同步問(wèn)題此外進(jìn)程間通信采用的方式是共享內(nèi)存。 參考文章 深刻理解Linux進(jìn)程間通信(IPC) 進(jìn)程間通信(IPC)介紹 php高級(jí)應(yīng)用之進(jìn)程控制及進(jìn)程間通訊 workman 作者發(fā)布 PHP 相關(guān)進(jìn)程間通信擴(kuò)展 -- System V ...
摘要:一進(jìn)程間通信理解間進(jìn)程通信機(jī)制,先了解下進(jìn)程間有哪些通訊機(jī)制歷史發(fā)展按照歷史來(lái)源主要有兩大塊的管道,,信號(hào)的消息隊(duì)列,共享內(nèi)存,信號(hào)燈。信號(hào)量主要作為進(jìn)程間,以及進(jìn)程內(nèi)部線程之間的通訊手段。主要依賴,兼容擴(kuò)展實(shí)現(xiàn)方式的進(jìn)程間通信之消息隊(duì)列。 PHP間進(jìn)程如何通信,PHP相關(guān)的服務(wù)的IPC是實(shí)現(xiàn)方式,IPC的思想如何用到項(xiàng)目中。 一、linux進(jìn)程間通信 理解php間進(jìn)程通信機(jī)制,先了解...
摘要:多進(jìn)程通信之一命名管道。多進(jìn)程通信之三信號(hào)量與共享內(nèi)存。共享內(nèi)存是最快是進(jìn)程間通信方式,因?yàn)閭€(gè)進(jìn)程之間并不需要數(shù)據(jù)復(fù)制,而是直接操控同一份數(shù)據(jù)。的一些書籍中甚至不建議新手輕易使用這種進(jìn)程間通信的方式,因?yàn)檫@是一種極易產(chǎn)生死鎖的解決方案。 [原文地址:https://blog.ti-node.com/blog...] 往往開(kāi)啟多進(jìn)程的目的是為了一起干活加速效率,前面說(shuō)了不同進(jìn)程之間的內(nèi)存...
摘要:是用編寫的,因此它對(duì)系統(tǒng)底層的操作與很像,同大多數(shù)語(yǔ)言一樣,進(jìn)程間通信的方式有以下幾種消息隊(duì)列,管道,共享內(nèi)存,和信號(hào)。創(chuàng)建管道創(chuàng)建進(jìn)程,子進(jìn)程寫管道,父進(jìn)程讀管道通過(guò)函數(shù)創(chuàng)建一個(gè)子進(jìn)程。當(dāng)返回時(shí)表示創(chuàng)建進(jìn)程失敗。 PHP是用C編寫的,因此它對(duì)系統(tǒng)底層API的操作與C很像,同大多數(shù)語(yǔ)言一樣,PHP進(jìn)程間通信的方式有以下幾種:消息隊(duì)列,管道,共享內(nèi)存,socket和信號(hào)。本文是對(duì)這幾種通...
閱讀 3041·2021-11-25 09:43
閱讀 3678·2021-11-24 11:13
閱讀 3438·2021-10-14 09:42
閱讀 2656·2021-09-23 11:53
閱讀 3680·2021-09-22 15:57
閱讀 3290·2021-09-02 09:54
閱讀 3561·2019-08-30 13:47
閱讀 1695·2019-08-29 16:55