摘要:擴展完成兼容機通用如獲取進程殺死進程等。擴展實現方式的進程間通信之消息隊列。四進程間通信通常中的進程通信方式有消息隊列信號量共享內存信號管道。下面這個例子中,父進程等待秒鐘,向子進程發送信號。子進程捕獲信號,掉信號處理函數處理。
一、引言
進程是一個具有獨立功能的程序關于某個數據集合的一次運行活動。換句話說就是,在系統調度多個cpu的時候,一個程序的基本單元。進程對于大多數的語言都不是一個陌生的概念,作為"世界上最好的語言PHP"當然也例外。
二、環境php中的進程是以擴展的形式來完成。通過這些擴展,我們能夠很輕松的完成進程的一系列動作。
pcntl擴展:主要的進程擴展,完成進程創建于等待操作。
posix擴展:完成posix兼容機通用api,如獲取進程id,殺死進程等。
sysvmsg擴展:實現system v方式的進程間通信之消息隊列。
sysvsem擴展:實現system v方式的信號量。
sysvshm擴展:實現system v方式的共享內存。
sockets擴展:實現socket通信。
這些擴展只能在linux/mac中使用,window下是不支持。最后建議php版本為5.5+。
相關代碼:進程相關代碼
三、簡單的例子一個簡單的PHP多進程例子,該例子中,一個子進程,一個父進程。子進程輸出5次,退出程序。
$parentPid = posix_getpid(); echo "parent progress pid:{$parentPid} "; $childList = array(); $pid = pcntl_fork(); if ( $pid == -1) { // 創建失敗 exit("fork progress error! "); } else if ($pid == 0) { // 子進程執行程序 $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 { // 父進程執行程序 $childList[$pid] = 1; } // 等待子進程結束 pcntl_wait($status); echo "({$parentPid})main progress end!";
完美,終于創建了一個子進程,一個父進程。完了么?沒有,各個進程之間相互獨立的,沒有任何交集,使用范圍嚴重受到現在。怎么辦,哪就進程間通信(interprogress communication)唄。
四、進程間通信(IPC)通常linux中的進程通信方式有:消息隊列、信號量、共享內存、信號、管道、socket。
1.消息隊列消息隊列是存放在內存中的一個隊列。如下代碼將創建3個生產者子進程,2個消費者子進程。這5個進程將通過消息隊列通信。
$parentPid = posix_getpid(); echo "parent progress pid:{$parentPid} ";$childList = array(); // 創建消息隊列,以及定義消息類型(類似于數據庫中的庫) $id = ftok(__FILE__,"m"); $msgQueue = msg_get_queue($id); const MSG_TYPE = 1; // 生產者 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); } } // 消費者 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) { // 創建失敗 exit("fork progress error! "); } else if ($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} "; } // 2個寫進程 for ($i = 0; $i < 2; $i ++ ) { $pid = createProgress("consumer"); $childList[$pid] = 1; echo "create consumer child progress: {$pid} "; } // 等待所有子進程結束 while(!empty($childList)){ $childPid = pcntl_wait($status); if ($childPid > 0){ unset($childList[$childPid]); } } echo "({$parentPid})main progress end! ";
由于消息隊列去數據是,只有一個進程能去到,所以不需要額外的鎖或信號量。
2. 信號量與共享內存信號量:是系統提供的一種原子操作,一個信號量,同時只有你個進程能操作。一個進程獲得了某個信號量,就必須被該進程釋放掉。
共享內存:是系統在內存中開辟的一塊公共的內存區域,任何一個進程都可以訪問,在同一時刻,可以有多個進程訪問該區域,為了保證數據的一致性,需要對該內存區域加鎖或信號量。
以下,創建多個進程修改內存中的同一個值。
$parentPid = posix_getpid(); echo "parent progress pid:{$parentPid} "; $childList = array(); // 創建共享內存,創建信號量,定義共享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; // 生產者 function producer(){ global $shareMemory; global $signal; $pid = posix_getpid(); $repeatNum = 5; for ( $i = 1; $i <= $repeatNum; $i++) { // 獲得信號量 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{ // 無值,初始化 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) { // 創建失敗 exit("fork progress error! "); } else if ($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! ";3.信號
信號是一種系統調用。通常我們用的kill命令就是發送某個信號給某個進程的。具體有哪些信號可以在liunx/mac中運行kill -l查看。下面這個例子中,父進程等待5秒鐘,向子進程發送sigint信號。子進程捕獲信號,掉信號處理函數處理。
$parentPid = posix_getpid(); echo "parent progress pid:{$parentPid} "; // 定義一個信號處理函數 function sighandler($signo) { $pid = posix_getpid(); echo "{$pid} progress,oh no ,I"m killed! "; exit(1); } $pid = pcntl_fork(); if ( $pid == -1) { // 創建失敗 exit("fork progress error! "); } else if ($pid == 0) { // 子進程執行程序 // 注冊信號處理函數 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{ // 父進程執行程序 $childList[$pid] = 1; // 5秒后,父進程向子進程發送sigint信號. sleep(5); posix_kill($pid,SIGINT); sleep(5); } echo "({$parentPid})main progress end! ";4.管道(有名管道)
管道是比較常用的多進程通信手段,管道分為無名管道與有名管道,無名管道只能用于具有親緣關系的進程間通信,而有名管道可以用于同一主機上任意進程。這里只介紹有名管道。下面的例子,子進程寫入數據,父進程讀取數據。
// 定義管道路徑,與創建管道 $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){ // 子進程,向管道寫數據 $file = fopen($pipe_path,"w"); while (true){ fwrite($file,"hello world"); $rand = rand(1,3); sleep($rand); } exit("child end!"); }else{ // 父進程,從管道讀數據 $file = fopen($pipe_path,"r"); while (true){ $rel = fread($file,20); echo "{$rel} "; $rand = rand(1,2); sleep($rand); } }5.socket
socket即我們常說的套接字編程。這個待補充。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/22105.html
摘要:接受不到消息消息隊列通過指定而被創建后,任意一方銷毀了該隊列,都會導致其他發送或接收方失敗。用法場景進程,中代碼段要用到中代碼段的結果。完成了進程間同步問題此外進程間通信采用的方式是共享內存。 參考文章 深刻理解Linux進程間通信(IPC) 進程間通信(IPC)介紹 php高級應用之進程控制及進程間通訊 workman 作者發布 PHP 相關進程間通信擴展 -- System V ...
摘要:多進程通信之一命名管道。多進程通信之三信號量與共享內存。共享內存是最快是進程間通信方式,因為個進程之間并不需要數據復制,而是直接操控同一份數據。的一些書籍中甚至不建議新手輕易使用這種進程間通信的方式,因為這是一種極易產生死鎖的解決方案。 [原文地址:https://blog.ti-node.com/blog...] 往往開啟多進程的目的是為了一起干活加速效率,前面說了不同進程之間的內存...
閱讀 2969·2021-11-25 09:43
閱讀 3586·2021-11-24 11:13
閱讀 3354·2021-10-14 09:42
閱讀 2556·2021-09-23 11:53
閱讀 3605·2021-09-22 15:57
閱讀 3221·2021-09-02 09:54
閱讀 3499·2019-08-30 13:47
閱讀 1638·2019-08-29 16:55