php進(jìn)程通信之共享內(nèi)存詳細(xì)講解
常見進(jìn)程通信方式
system V共享內(nèi)存
現(xiàn)代操作系統(tǒng),對(duì)于內(nèi)存管理,采用的是虛擬內(nèi)存技術(shù),也就是每個(gè)進(jìn)程都有自己獨(dú)立的虛擬內(nèi)存空間,不同進(jìn)程的虛擬內(nèi)存映射到不同的物理內(nèi)存中。所以,即使進(jìn)程 A 和 進(jìn)程 B 的虛擬地址是一樣的,其實(shí)訪問的是不同的物理內(nèi)存地址,對(duì)于數(shù)據(jù)的增刪查改互不影響。
共享內(nèi)存的機(jī)制,就是拿出一塊虛擬地址空間來,映射到相同的物理內(nèi)存中。這樣這個(gè)進(jìn)程寫入的東西,另外一個(gè)進(jìn)程馬上就能看到了,不需要經(jīng)過數(shù)次的拷貝(比如從輸入緩沖區(qū)中拷貝到文件中、再拷貝到輸出緩沖區(qū)中等),大大提高了進(jìn)程間通信的速度。
在所有進(jìn)程間通信的方式中共享內(nèi)存的效率是最高的。
共享內(nèi)存操作默認(rèn)不阻塞,如果多個(gè)進(jìn)程同時(shí)讀寫共享內(nèi)存,可能出現(xiàn)數(shù)據(jù)混亂,共享內(nèi)存需要借助其他機(jī)制來保證進(jìn)程間的數(shù)據(jù)同步,比如:上期講信號(hào)量,共享內(nèi)存內(nèi)部沒有提供這種同步機(jī)制。
通過上圖可知,共享內(nèi)存是通過將不同進(jìn)程的虛擬內(nèi)存地址映射到相同的物理內(nèi)存地址來實(shí)現(xiàn)的
頁表是一種特殊的數(shù)據(jù)結(jié)構(gòu),放在系統(tǒng)空間的頁表區(qū),存放邏輯頁(虛擬內(nèi)存)與物理頁幀(物理內(nèi)存)的對(duì)應(yīng)關(guān)系。 每一個(gè)進(jìn)程都擁有一個(gè)自己的頁表。
php使用共享內(nèi)存
php 提供了兩套操作共享內(nèi)存的擴(kuò)展,兩套擴(kuò)展都實(shí)現(xiàn)了相同的功能,用哪個(gè)看你喜歡
https://www.php.net/manual/en/ref.shmop.php
https://www.php.net/manual/en/ref.sem.php
共享內(nèi)存基本函數(shù)使用
<?php $key = ftok('demo1.php','x'); //將路徑名和項(xiàng)目標(biāo)識(shí)符轉(zhuǎn)換為System V IPC鍵 $shm_id = shm_attach($key,1024,0666);//創(chuàng)建一個(gè)大小為1024字節(jié)的共享內(nèi)存存段,權(quán)限為0666,并且將共享內(nèi)存映射關(guān)聯(lián)到當(dāng)前進(jìn)程的地址空間 shm_put_var($shm_id,1,'hello world');//往共享內(nèi)存里寫入數(shù)據(jù) echo "共享內(nèi)存:".shm_get_var($shm_id,1).PHP_EOL;//讀取共享內(nèi)存里數(shù)據(jù)
父子進(jìn)程通信
<?php $key = ftok('demo4.php','x'); // $shm_id 它對(duì)應(yīng)當(dāng)前進(jìn)程的地址空間,實(shí)際是映射連接了系統(tǒng)分配的共享區(qū)域(共享內(nèi)存) $shm_id = shm_attach($key,1024,0666);//創(chuàng)建一個(gè)大小為1024字節(jié)的共享內(nèi)存存段,權(quán)限為0666,并且將共享內(nèi)存映射關(guān)聯(lián)到當(dāng)前進(jìn)程的地址空間 $pid = pcntl_fork();// fork 子進(jìn)程 if($pid == 0){//子進(jìn)程運(yùn)行邏輯 echo shm_get_var($shm_id,1).PHP_EOL;//讀取共享內(nèi)存數(shù)據(jù) exit(0); } //父進(jìn)程運(yùn)行邏輯 shm_put_var($shm_id,1,'hello world');//寫入數(shù)據(jù)到共享內(nèi)存,第三個(gè)參數(shù)可以傳入任意數(shù)據(jù)類型 pcntl_wait($status);//等待子進(jìn)程退出釋放資源,防止僵尸進(jìn)程 // 刪除共享內(nèi)存是有順序的,先remove后detach,順序反過來php可能會(huì)報(bào)錯(cuò) shm_remove($shm_id);//從系統(tǒng)中移除共享內(nèi)存 shm_detach($shm_id);//斷開進(jìn)程與共享內(nèi)存的映射關(guān)系
配合信號(hào)量使用
<?php // sem key $sem_key = ftok( __FILE__, 'b' );//信號(hào)量key $sem_id = sem_get( $sem_key );//獲取信號(hào)量 // shm key $shm_key = ftok( __FILE__, 'm' );//共享內(nèi)存key $shm_id = shm_attach( $shm_key, 1024, 0666 );//創(chuàng)建一個(gè)大小為1024字節(jié)的共享內(nèi)存存段,權(quán)限為0666,并且將共享內(nèi)存映射關(guān)聯(lián)到當(dāng)前進(jìn)程的地址空間 $child_pid = []; // fork 2 子進(jìn)程 for( $i = 1; $i <= 2; $i++ ){ $pid = pcntl_fork(); if( $pid < 0 ){ exit(); } else if( 0 == $pid ) {//子進(jìn)程運(yùn)行邏輯 // 獲取鎖 sem_acquire( $sem_id ); // 檢測(cè)共享內(nèi)存是否有值 if( shm_has_var( $shm_id, 1) ){ //shm_get_var第二參數(shù)必須是int型 $counter = shm_get_var( $shm_id, 1); $counter += 1; shm_put_var( $shm_id, 1, $counter );//寫入共享內(nèi)存 } else { $counter = 1; shm_put_var( $shm_id, 1, $counter );//寫入共享內(nèi)存 } // 釋放鎖,一定要記得釋放,不然就一直會(huì)被阻鎖死 sem_release( $sem_id ); exit; } else if( $pid > 0 ) {//父進(jìn)程運(yùn)行邏輯 $child_pid[] = $pid; } } while( !empty( $child_pid ) ){ foreach( $child_pid as $pid_key => $pid_item ){ $wait_result=pcntl_waitpid( $pid_item, $status, WNOHANG ); //必須判斷子進(jìn)程回收的狀態(tài),如果不加判斷,第一次兩個(gè)子進(jìn)程返回都是0,直接unset后會(huì)無法進(jìn)入while,導(dǎo)致僵尸進(jìn)程 if($wait_result == -1 || $wait_result > 0) unset( $child_pid[ $pid_key ] ); } } // 休眠2秒鐘,2個(gè)子進(jìn)程都執(zhí)行完畢了 sleep( 2 ); echo '最終結(jié)果'.shm_get_var( $shm_id, 1 ).PHP_EOL; // 刪除共享內(nèi)存是有順序的,先remove后detach,順序反過來php可能會(huì)報(bào)錯(cuò) shm_remove( $shm_id ); shm_detach( $shm_id );
運(yùn)行結(jié)果:
如果不使用信號(hào)量,運(yùn)行結(jié)果有一定概率會(huì)產(chǎn)生1而不是2。但是只要加入信號(hào)量sem,就一定保證100%是2,絕對(duì)不會(huì)出現(xiàn)其他數(shù)值。
非血緣關(guān)系進(jìn)程共享內(nèi)存通信
我們使用php 提供的另一套擴(kuò)展來實(shí)現(xiàn)
寫進(jìn)程
<?php $key = ftok('a.php','x'); $shm_id = shmop_open($key,'c',0664,1204);//創(chuàng)建一個(gè)大小為1024字節(jié)的共享內(nèi)存存段,權(quán)限為0666,并且將共享內(nèi)存映射關(guān)聯(lián)到當(dāng)前進(jìn)程的地址空間, 第二個(gè)參數(shù)為 'c' 表示共享內(nèi)存段不存在就創(chuàng)建,存在就直接連接打開 shmop_write($shm_id,'學(xué)無止境',0); // 寫入數(shù)據(jù) ,第三個(gè)參數(shù)代表寫入數(shù)據(jù)的偏移量
讀進(jìn)程
<?php $key = ftok('a.php','x'); $shm_id = shmop_open($key,'c',0664,1204);//創(chuàng)建一個(gè)大小為1024字節(jié)的共享內(nèi)存存段,權(quán)限為0666,并且將共享內(nèi)存映射關(guān)聯(lián)到當(dāng)前進(jìn)程的地址空間, 第二個(gè)參數(shù)為 'c' 表示共享內(nèi)存段不存在就創(chuàng)建,存在就直接連接打開 echo shmop_read($shm_id,0,20).PHP_EOL; //讀取共享段內(nèi)容,第二個(gè)參數(shù)讀取內(nèi)存偏移量,第三個(gè)參數(shù)是要讀取的字節(jié)數(shù) shmop_delete($shm_id);//從系統(tǒng)中刪除共享內(nèi)存段
共享內(nèi)存的特性
生命周期跟隨操作系統(tǒng)
可以通過ipcs
命令查看共享內(nèi)存
共享內(nèi)存采用的是覆蓋寫的方式,讀的時(shí)候,是訪問地址。
覆蓋寫可以理解為,再次往共享內(nèi)存中寫的時(shí)候,會(huì)先將共享內(nèi)存中的內(nèi)容清空,再寫入。
讀的時(shí)候, 是訪問地址,區(qū)別于管道是將數(shù)據(jù)讀走了,而在共享內(nèi)存中讀的時(shí)候并沒有將數(shù)據(jù)讀走,僅僅是訪問地址。
到此這篇關(guān)于php進(jìn)程通信之共享內(nèi)存詳細(xì)講解的文章就介紹到這了,更多相關(guān)php共享內(nèi)存內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
php中mail函數(shù)發(fā)送郵件失敗的解決方法
這篇文章主要介紹了php中mail函數(shù)發(fā)送郵件失敗的解決方法,涉及針對(duì)Linux運(yùn)行平臺(tái)相關(guān)組件的配置技巧,具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2014-12-12php實(shí)現(xiàn)對(duì)兩個(gè)數(shù)組進(jìn)行減法操作的方法
這篇文章主要介紹了php實(shí)現(xiàn)對(duì)兩個(gè)數(shù)組進(jìn)行減法操作的方法,涉及php操作數(shù)組的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-04-04PHP+Mysql日期時(shí)間如何轉(zhuǎn)換(UNIX時(shí)間戳和格式化日期)
UNIX時(shí)間戳和格式化日期是我們常打交道的兩個(gè)時(shí)間表示形式,Unix時(shí)間戳存儲(chǔ)、處理方便,但是不直觀,格式化日期直觀,但是處理起來不如Unix時(shí)間戳那么自如,所以有的時(shí)候需要互相轉(zhuǎn)換,下面給出互相轉(zhuǎn)換的幾種轉(zhuǎn)換方式2012-07-07高質(zhì)量PHP代碼的50個(gè)實(shí)用技巧必備(下)
這篇文章主要為大家分享了50個(gè)高質(zhì)量PHP代碼的實(shí)用技巧,大家必備的php實(shí)用代碼,感興趣的小伙伴們可以參考一下2016-01-01