共享內存的使用主要是為了能夠在同一台機器不同的進程中共享一些數據,比如在多個 php-fpm 進程中共享當前進程的使用情況。這種通信也稱為進程間通信(Inter-Process Communication),簡稱 IPC。
PHP 內置的 shmop 擴展 (Shared Memory Operations) 提供了一系列共享內存操作的函數(可能是用的人不多吧,這一塊兒的文檔還沒有中文翻譯)。在 Linux 上,這些函數直接是通過調用 shm* 系列的函數實現,而 Winodows 上也通過對系統函數的封裝實現了同樣的調用。
主要函數:
與此相關的還有一個很重要的函數:ftok,通過文件的 inode 信息(*nix 上通過 stat
或 ls -i
命令查看)創建 IPC 的唯一 key(文件/文件夾的 inode 是唯一的)。這個函數在 Linux 上也是直接調用同名的系統函數實現,Windows 上還是使用一些封裝。
一個簡單的計數例子:
<?php
# 創建一塊共享內存
$shm_key = ftok(__FILE__, 't');
$shm_id = shmop_open($shm_key, 'c', 0644, 8);
# 讀取並寫入數據
$count = (int) shmop_read($shm_id, 0, 8) + 1;
shmop_write($shm_id, str_pad($count, 8, '0', STR_PAD_LEFT), 0);
// echo shmop_read($shm_id, 0, 8);
# 關閉內存塊,並不會刪除共享內存,只是清除 PHP 的資源
shmop_close($shm_id);
以上這段代碼沒執行一次計數加 1,而且數據是在不同進程之間共享的。也就是說除非手動刪除這塊內存使用,否則這個數據是不會重置的。
有個需要稍微注意的點:shmop_open
的第二個參數是個 flag,類似 fopen 的第二個參數,其取值有以前幾個:
此外,由於使用的共享內存片段是固定長度的,在存儲和讀取的時候要計算好數據的長度,不然可能會寫入失敗或者讀取空值。
既然上面使用到了共享內存存儲數據,就需要考慮是否有多個進程同時寫入數據到共享內存的情況,是否需要避免沖突。如果是這樣,就需要引入信號量進行控制。
PHP 也提供了類似的內置擴展 sysvsem(這個擴展在 Windows 環境下沒有,文檔中將 ftok
函數也歸到這個擴展中,但實際上 ftok
是在標准函數庫中提供的,所以在 Windows 下也是可用的)。
在說信號量控制之前,先說另外一件有意思的事情:看官方文檔你會發現這裡同樣也有共享內存操作的函數(shm_*
),因為這其實是同一類別(或者說來自於同一作者)的三個擴展,還有一個是 sysvmsg(隊列消息) 。函數的實現上稍有差別,但實際做的事情基本相同。這和上文的 shmop 擴展有什麼區別呢?shmop 源碼下的 README
文件有簡單的說明:
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 擴展提供的方法並不是原封不動的存儲用戶的數據,而是先使用 PHP 的變量序列化函數對參數進行序列化然後再進行存儲。這就導致通過這些方法存儲的數據無法和非 PHP 進程共享。不過這樣也能存儲更豐富的 PHP 數據類型,上文的擴展中 shmop_write
只能寫入字符串。那麼為什麼 sysvshm 同樣不支持 Windows 呢?因為其並沒有引入封裝了 shm*
系列函數的 tsrm_win32.h
的頭文件。
引入信號控制之後的示例:
<?php
$id_key = ftok(__FILE__, 't');
$sem_id = sem_get($id_key);
# 請求信號控制權
if (sem_acquire($sem_id)) {
$shm_id = shmop_open($id_key, 'c', 0644, 8);
# 讀取並寫入數據
$count = (int) shmop_read($shm_id, 0, 8) + 1;
shmop_write($shm_id, str_pad($count, 8, '0', STR_PAD_LEFT), 0);
// echo shmop_read($shm_id, 0, 8);
# 關閉內存塊
shmop_close($shm_id);
# 釋放信號
sem_release($sem_id);
}
但是本地想模擬實現寫入沖突實際上是非常難的(考慮到計算機的執行速度)。在本地測試中,使用 for
循環操作時如果不使用 shmop_close
關閉資源會出現無法打開共享內存的錯誤警告。這應該是因為正在共享內存被上一次操作占用中還沒有釋放導致。
PHP 7革新與性能優化 http://www.linuxidc.com/Linux/2015-09/123136.htm
PHP 7 ,你值得擁有 http://www.linuxidc.com/Linux/2015-06/118847.htm
在 CentOS 7.x / Fedora 21 上面體驗 PHP 7.0 http://www.linuxidc.com/Linux/2015-05/117960.htm
CentOS 6.3 安裝LNMP (PHP 5.4,MyySQL5.6) http://www.linuxidc.com/Linux/2013-04/82069.htm
在部署LNMP的時候遇到Nginx啟動失敗的2個問題 http://www.linuxidc.com/Linux/2013-03/81120.htm
Ubuntu安裝Nginx php5-fpm MySQL(LNMP環境搭建) http://www.linuxidc.com/Linux/2012-10/72458.htm
《細說PHP》高清掃描PDF+光盤源碼+全套教學視頻 http://www.linuxidc.com/Linux/2014-03/97536.htm
CentOS 6中配置PHP的LNMP的開發環境 http://www.linuxidc.com/Linux/2013-12/93869.htm
PHP 的詳細介紹:請點這裡
PHP 的下載地址:請點這裡