1. 程式人生 > >PHP共享內存

PHP共享內存

無法打開 應用 get 一個 系列 car off lose extension

如何使用 PHP shmop 創建和操作共享內存段,使用它們存儲可供其他應用程序使用的數據。

1. 創建內存段

共享內存函數類似於文件操作函數,但無需處理一個流,您將處理一個共享內存訪問 ID。第一個示例就是 shmopopen 函數,它允許您打開一個現有的內存段或創建一個新內存段。此函數非常類似於經典的 fopen 函數,後者打開用於文件操作的流,返回一個資源供其他希望讀取或寫入該打開的流的函數使用。讓我們看看 shmopopen的用法:

<?php  
$key = ftok(__FILE__, ‘h‘);
$mode = ‘c‘;
$permissions = 0644;
$size = 1024;
$shmid = shmop_open($key, $mode, $permissions, $size);
?>

第一個參數($key):

系統建立IPC通訊 (消息隊列、信號量和共享內存) 時必須指定一個key值。通常情況下,該key值通過ftok函數得到, * *key是一個我們邏輯上表示共享內存段的標識。不同進程只要選擇同一個Key值就可以共享同一段存儲段。

第二個參數($mode):

訪問模式,它類似於fopen的訪問模式,有以下幾種

  • 模式 “a”,它允許您訪問只讀內存段
  • 模式 “w”,它允許您訪問可讀寫的內存段
  • 模式 “c”,它創建一個新內存段,或者如果該內存段已存在,嘗試打開它進行讀寫 *模式 “n”,它創建一個新內存段,如果該內存段已存在,則會失敗,返回 false,並伴隨有warning: unable to attach or create shared memory segment

第三個參數($permissions):

內存段的權限。您必須在這裏提供一個八進制值,它類似於UNIX操作系統文件和目錄的操作權限。

第四個參數($size):

內存段大小,以字節為單位。在寫入一個內存段之前,您必須在它之上分配適當的字節數。

返回結果:

此函數返回一個 ID 編號,其他函數可使用該 ID 編號操作該共享內存段。這個 ID 是共享內存訪問 ID,與系統 ID 不同,它以參數的形式傳遞。請註意不要混淆這兩者。如果失敗,shmop_open 將返回 FALSE。

shmop_open成功後,使用ipcs -m, 可以查看到剛剛創建的內存段,註意 申請的內存段有嚴格的權限,比如用root用戶申請的,普通用戶就無權訪問

2. 向內存段寫入數據

使用 shmop_write 函數向共享內存塊寫入數據。此函數的使用很簡單,它僅接受 3 個參數,如下所示。

<?php  
//這裏shmid可以延用上一段代碼返回的shmid
$shmid = shmop_open(ftok(__FILE__,‘h‘), ‘c‘, 0644, 1024); 
shmop_write($shmid, "Hello World!", 0);

?>

這個函數類似於 fwrite 函數, 在這裏有三個參數。 * 第一個參數(shmidshmopopenID?shmid):是shmopopen返回的ID,它識別您操作的共享內存塊。?第二個參數(data):是您希望存儲的數據。 * 第三個參數($offset):是您希望開始寫入的位置。默認情況下,我們始終使用 0 來表示開始寫入的位置。

返回結果:此函數在失敗時會返回 FALSE,在成功時會返回寫入的字節數。

3. 從內存段讀取數據

從共享內存段讀取數據很簡單。您只需要一個打開的內存段和 shmop_read 函數,它接受三個參數,如下所示:

<?php  
$shmid = shmop_open(ftok(\__FILE_\_,‘h‘), ‘c‘, 0644, 1024);
shmop_write($shmid, "Hello World\!", 0);  
var_dump(shmop_read($shmid, 0, 11));  
?>
  • 第一個參數($shmid):是 shmop_open 返回的 ID,它識別您操作的共享內存塊。
  • 第二個參數($start):是您希望從內存段讀取的位置,這個參數可以始終為0, 表示數據的開頭
  • 第三個參數(countshmopsize(count):是您希望讀取的字節數。一般情況下我們用shmopsize(shmid),以便完整的讀取它。

4. 刪除內存段

shmop_delete 該函數只接收一個參數,如下所示:

<?php  
$shmid = shmop_open(ftok(\__FILE_\_,‘h‘), ‘c‘, 0644, 1024);
shmop_delete($shmid);  
?>

其實這個函數不會實際刪除該內存段。它將該內存段標記為刪除狀態,因為共享內存段在有其他進程正在使用它時無法被刪除。shmop_delete 函數將該內存段標記為刪除,阻止任何其他進程打開它。要刪除它,我們需要關閉該內存段。

5. 關閉內存段

打開一個共享內存段會 “附加” 到它。附加該內存段之後,我們可在其中進行讀取和寫入,但完成操作後,我們必須從它解除。

<?php  
$shmid = shmop_open(ftok(\__FILE_\_,‘h‘), ‘c‘, 0644, 1024);
shmop_write($shmid, "Hello World\!", 0);  
shmop_delete($shmid); shmop_close($shmid);  
?>

共享內存的原子操作 - 信號控制

針對共享內存的寫操作本身不是原子性的,那麽當我們大量並發進行讀寫的時候,怎麽保證原子性呢,這裏要引入信號量進行控制。

PHP 也提供了內置擴展 sysvsem ,其實我們在看sysvsem 提供的一系列sem*的方法的時候,就會想到,這和上面提到的shmop*有什麽區別呢,我們來看官房文檔中的這一個解釋:PHP already had a shared memory extension (sysvshm) written by Christian Cartus [email protected], unfortunately this extension was designed with PHP only in mind and offers high level features which are extremely bothersome for basic SHM we had in mind.

也就是說:sysvshm 擴展提供的方法在存儲之前對用戶的數據進行serialize處理,這裏就導致這個存儲的數據是無法與其它語言共享的,這一系列方法是php only的方法。

引入信號控制之後的示例:

<?php  
$key = ftok(_FILE_, ‘h‘) $mode = "c";
$permissions = 0755;
$size = 1024; // 內存段的大小,單位是字節
$semid = sem_get($key); # 請求信號控制權
if (sem_acquire($semid)) {  
    $shmid = shmop_open($key, ‘c‘, 0644, 1024); # 讀取並寫入數據
    shmop_write($shmid, ‘13800138000‘, 0); # 關閉內存塊
    shmop_close($shmid); # 釋放信號 sem_release($semid);
}

共享內存的操作是非常快的,在本地想要模擬實現寫入沖突是非常困難的,但是本地想模擬實現寫入沖突實際上是非常難的(考慮到計算機的執行速度)。在本地測試中,使用 for 循環操作時如果不使用shmop_close 關閉資源會出現無法打開共享內存的錯誤警告。這應該是因為正在共享內存被上一次操作占用中還沒有釋放導致。

PHP共享內存