從併發處理談PHP進程間通訊(二)System V IPC

前言

進程間通訊是一個永遠的話題,個人上一篇文章經過一個併發循環ID生成器的實現介紹瞭如何使用外部介質來進行進程間通訊:從併發處理談PHP進程間通訊(一)外部介質 。介紹的幾種方法適用於各類語言,可是他們都依賴於一種外部介質,文化的讀寫有瓶頸,mysql 和 redis 會掛掉或鏈接超時,歸根結底總以爲在 HACK;javascript

對於進程間通訊,每個完備的語言都應該有對應的處理方式,而 PHP 對應的則是一族對 UNIX SYSTEM V包裝的函數,包括信號量(semaphore)、共享內存(shared memory)和消息隊列(msg queue)的操做。php

它的安裝和使用很是簡單,在編譯 PHP 時添加 --enable-sysvsem --enable-sysvshm --enable-sysvmsg 參數就能夠,固然 Windows 上沒法使用。css

今天咱們仍舊使用上一篇文章的例子來介紹 PHP 內部實現的進程間通訊,在瞭解它們的具體使用以前,先簡單介紹一下信號量、共享內存、消息隊列的概念。html


Unix System V IPC

信號量

信號量又稱爲信號燈,它是用來協調不一樣進程間的數據對象的,而最主要的應用是共享內存方式的進程間通訊。本質上,信號量是一個計數器,它用來記錄對某個資源(如共享內存)的存取情況。java

通常說來,爲了得到共享資源,進程須要執行下列操做:python

  1. 獲取控制共享資源的信號量的值;
  2. 若值爲正,進程將信號量減1,進程操做共享資源,進入步驟4;
  3. 若值0,則拒絕進程使用共享資源,進程進入睡眠狀態,直至信號量值大於0後,進程被喚醒,轉入步驟1;
  4. 當進程再也不使用共享資源時,將信號量值加1。若是此時有進程正在睡眠等待此信號量,則喚醒此進程;

信號量的使用能夠類比爲:mysql

一個房間必須用鑰匙才能開門,有N把鑰匙放在門口,拿到鑰匙開門進入房間,出來時將鑰匙放回並告知等待的人去取鑰匙開門。 此例中,鑰匙的數量限制了同一時間內在房間的最大人數。房間即共享資源,鑰匙是信號量,而想進入房間的人則是多個進程。nginx

信號量有二值和多值之分,通常共享資源都不容許多個進程同時操做,多使用二值信號量。git

共享內存

爲了在多個進程間交換信息,內核專門留出了一塊內存區,能夠由須要訪問的進程將其映射到本身的私有地址空間。進程就能夠直接讀寫這一塊內存而不須要進行數據的拷貝,從而大大提升效率。共享內存能夠比喻成一塊公用黑板,每一個人都能在上面留言,寫東西。github

到於共享內存,咱們必定要關心其生存週期:System V 共享內存區域對象是隨內核持續的,除非顯式刪除共享內存區域對象,即便全部訪問共享內存區域對象的進程都已經正常結束,共享內存區域對象仍然在內核中存在,在內核從新引導以前,對該共享內存區域對象的任何改寫操做都將一直保留。

消息隊列

消息隊列是一條公共消息鏈,消息存取通常爲先進先出(FIFO),能實現多個進程對消息的原子操做和異步存取。消息隊列的應用十分普遍,不光是進程間通訊,流程異步化、解耦方面也應用普遍。

消息隊列則至關於一條流水線的一段,上層有多個工人把產品放入,下層有多個工人將產品取出加工。

本文的實現不包括消息隊列的使用,但對於消息隊列實現互斥鎖,這裏給出一個思路:先給消息隊列初始化一個值,併發進程競爭獲取此值,獲取到值的進程進行共享資源的處理,進程再也不共享資源時,再將此值放入隊列,經過隊列的原子性來保證同時只有一個進程訪問共享資源。


函數介紹

ftok

int ftok ( string $pathname, string $proj )

ftok將一個路徑 pathname 和一個項目名(必須爲一個字符), 轉化成一個整數形的 System V IPC 鍵,本文介紹的 System V 通訊方式都是基於此鍵來完成的,此ID 值也能夠本身指定一個 INT 型來肯定,沒必要要使用 ftok 獲取;

須要注意的是:ftok 的結果是經過文檔的索引節點號來計算獲取的,而文件的刪除重建會致使其索引節點號變更,因此即便是相同的文件名,也可能會致使獲取到的 IPC 鍵不一樣,因此須要儘可能保證 $pathname 不變更;

semaphore函數

  • resource sem_get ( int $key [, int $max_acquire = 1 [, int $perm = 0666 [, int $auto_release = 1 ]]] )

    獲取或生成一個信號量標識,咱們注意其 max_acquire 值爲 1,即保證同時只有一個進程能獲取到它;auto_release 爲 1 ,保證進程在非正常狀況退出時能釋放此信號量;

  • bool sem_acquire ( resource $sem_identifier [, bool $nowait = false ] )

    bool sem_release ( resource $sem_identifier )

    獲取/釋放一個信號量,注意獲取信號量的 $nowait 爲false,使進程在獲取信號量失敗後進行進程等待便可。

shared_memory函數

  • resource shm_attach ( int $key [, int $memsize [, int $perm = 0666 ]] )

  • bool shm_detach ( resource $shm_identifier )

    鏈接/斷開 與 共享內存段的鏈接 $memsize, 以字節 byte 爲單位;須要注意,在第一次使用 $key 鏈接內存段建立時,會初始化內存大小和權限,後續再鏈接時,這兩個參數會被忽略。

  • bool shm_put_var ( resource $shm_identifier , int $variable_key , mixed $variable )

  • mixed shm_get_var ( resource $shm_identifier , int $variable_key )

    向共享內存內寫入或讀取一個變量,須要注意變量 key 只能是 int 型;


代碼實現

function getCycleIdFromSystemV($max, $min = 0) {
        $key = ftok('/tmp/cycleIdFromSystemV.tok', 'd');
        $var_key = 0;
        $sem_id = sem_get($key);
        $shm_id = shm_attach($key, 4096);
    
        if (sem_acquire($sem_id)) {
            $cycle_id = intval(shm_get_var($shm_id, $var_key));
            $cycle_id++;
            if ($cycle_id > $max) {
                $cycle_id = $min;
            }
            shm_put_var($shm_id, $var_key, $cycle_id);
    
            shm_detach($shm_id);
            sem_release($sem_id);
    
            return $cycle_id;
        }
        
        return false;
    }

小結

咱們發現 PHP 對信號量和共享內存封裝得很好,使用起來很是簡單。除此以外,PHP 的類庫 Sync 將經常使用 IPC 方法封裝成爲類,能實現跨平臺的使用,感興趣的能夠了解使用一下。

固然進程間通訊的方式和種類有不少,本文介紹的 id 遞增只是很簡單的一種,不過,知道了方法,再去把這些方法改形成爲其餘種類也就不難了。

若是您以爲本文對您有幫助,能夠點擊下面的 推薦 支持一下我。博客一直在更新,歡迎 關注

相關文章
相關標籤/搜索