由於這裡沒有提到(缺少)鎖定的需求,我查看了 shmop.c 擴充功能的程式碼。如果我沒有理解錯誤,shmop.c 擴充功能使用 memcpy() 來複製字串進出共享記憶體,而沒有任何形式的鎖定,而且據我所知,memcpy() 不是原子性的。
如果如我懷疑的那樣是正確的,那麼這些「易於使用」的函式就沒那麼「易於使用」了,必須用鎖定包裝(例如,信號量、flocks 或其他)。
由於這裡沒有提到(缺少)鎖定的需求,我查看了 shmop.c 擴充功能的程式碼。如果我沒有理解錯誤,shmop.c 擴充功能使用 memcpy() 來複製字串進出共享記憶體,而沒有任何形式的鎖定,而且據我所知,memcpy() 不是原子性的。
如果如我懷疑的那樣是正確的,那麼這些「易於使用」的函式就沒那麼「易於使用」了,必須用鎖定包裝(例如,信號量、flocks 或其他)。
我在 2003 年寫了一個 php memcache 作為概念證明
它被用於幾台機器上,進行大量的頁面載入快取...
它運作良好。
以下是我製作的一些核心函式
<?php
###############################################
#### 共享記憶體函式
/*
為了除錯這些
使用 `ipcs` 查看目前的記憶體
使用 `ipcrm -m {shmid}` 移除
在某些系統上,如果您不想手動清除,請使用 `ipcclean` 清除未使用的記憶體
*/
###############################################
function get_key($fsize, $file){
if(!file_exists(TMPDIR.TMPPRE.$file)){
touch(TMPDIR.TMPPRE.$file);
}
$shmkey = @shmop_open(ftok(TMPDIR.TMPPRE.$file, 'R'), "c", 0644, $fsize);
if(!$shmkey) {
return false;
}else{
return $shmkey;
}//fi
}
function writemem($fdata, $shmkey){
if(MEMCOMPRESS && function_exists('gzcompress')){
$fdata = @gzcompress($fdata, MEMCOMPRESSLVL);
}
$fsize = strlen($fdata);
$shm_bytes_written = shmop_write($shmkey, $fdata, 0);
updatestats($shm_bytes_written, "add");
if($shm_bytes_written != $fsize) {
return false;
}else{
return $shm_bytes_written;
}//fi
}
function readmem($shmkey, $shm_size){
$my_string = @shmop_read($shmkey, 0, $shm_size);
if(MEMCOMPRESS && function_exists('gzuncompress')){
$my_string = @gzuncompress($my_string);
}
if(!$my_string) {
return false;
}else{
return $my_string;
}//fi
}
function deletemem($shmkey){
$size = @shmop_size($shmkey);
if($size > 0){ updatestats($size, "del"); }
if(!@shmop_delete($shmkey)) {
@shmop_close($shmkey);
return false;
}else{
@shmop_close($shmkey);
return true;
}
}
function closemem($shmkey){
if(!shmop_close($shmkey)) {
return false;
}else{
return true;
}
}
function iskey($size, $key){
if($ret = get_key($size, $key)){
return $ret;
}else{
return false;
}
}
################################################
?>
我寫了一個腳本來突顯共享記憶體儲存的優越性。
雖然它沒有使用 shmop 函式,但底層概念是相似的。
'/shm_dir/' 是一個 tmpfs 目錄,它是基於我已掛載在伺服器上的共享記憶體。
以下是 Intel Pentium VI 2.8 伺服器上的結果
在 1000 個檔案上的 IO 測試
常規目錄的 IO 結果:0.079015016555786
共享記憶體目錄的 IO 結果:0.047761917114258
在 10000 個檔案上的 IO 測試
常規目錄的 IO 結果:3.7090260982513
共享記憶體目錄的 IO 結果:0.46256303787231
在 40000 個檔案上的 IO 測試
常規目錄的 IO 結果:117.35703110695 秒
共享記憶體目錄的 IO 結果:2.6221358776093 秒
在 100 個檔案時,差異不是很明顯,也沒有說服力。
但當我們將其提升到 10000 和 40000 個檔案的程度時,很明顯共享記憶體是一個更好的競爭者。
腳本由 http://www.enhost.com 提供
<?php
set_time_limit(0);
// 您的常規目錄。請確保它具有寫入權限
$setting['regular_dir'] = '/home/user/regular_directory/';
// 您的共享記憶體目錄。
$setting['shm_dir'] = '/shm_dir/';
// 要讀寫的檔案數量
$setting['files'] = 40000;
function IO_Test($mode)
{
$starttime = time()+microtime();
global $setting;
for($i = 0 ; $i< $setting['files'] ;$i++)
{
$filename = $setting[$mode].'test'.$i.'.txt';
$content = "Just a random content";
// 僅進行一些錯誤檢測
if (!$handle = fopen($filename, 'w+'))
{
echo "無法開啟檔案 ".$filename;
exit;
}
if (fwrite($handle, $content ) === FALSE)
{
echo "無法寫入檔案 : ".$filename;
exit;
}
fclose($handle);
// 讀取測試
file_get_contents($filename);
}
$endtime = time()+microtime();
$totaltime = ($endtime - $starttime);
return $totaltime;
}
echo '<b>在 '.$setting['files']. ' 個檔案上進行 IO 測試</b><br>';
echo '<b>常規</b>目錄的 IO 結果 : '.IO_Test('regular_dir') .' 秒<br>';
echo '<b>共享記憶體</b>目錄的 IO 結果 : '.IO_Test('shm_dir') .' 秒<br>';
/* 移除檔案以避免低估
#
# 未能移除檔案將導致基準測試不準確
# 因為這將導致 IO_Test 函數不會重新建立現有的檔案
*/
foreach ( glob($setting['regular_dir']."*.txt") as $filename) {
unlink($filename);$cnt ++;
}
foreach ( glob($setting['shm_dir']."*.txt") as $filename) {
unlink($filename);$cnt ++;
}
?>
Windows 通過記憶體對應檔案支援共享記憶體。 檢查以下函數以獲取詳細資訊
* CreateFileMapping
* MapViewOfFile
此說明頁面中描述的 shmop 實作實際上僅是一個 ramdisk / tmpfs,它僅存在於 php 內部,甚至僅存在於 Linux 伺服器上。還是我遺漏了什麼?
在 Windows 上,可以通過建立這樣的磁碟輕鬆實現相同的功能。
事實上,在我自己的伺服器上,我確實使用 tmpfs 磁碟來代替 - 在我看來 - 有限的 shmop 功能。
為什麼不實作一個 $_SHARED 或 $_MUTUAL 超級全域變數,我們可以在其中隨意建立變數,並由所有連線共享?
這將大大提高許多 PHP 應用程式的效能,並且可以節省伺服器記憶體上的大量負擔。 特別是如果這些變數可以是包含函數的類別。
您可以實作由程式設計師來保護原子性。
這種超級全域變數在 Windows 伺服器上也是可行的。
SHMOP 背後的想法是一個易於使用的共享記憶體介面,
而不在共享記憶體段中新增任何額外的標頭
或需要任何特殊的特殊控制來存取 PHP 以外的共享記憶體
段。SHMOP 從 C 的 shm API 中借用了其 API,
這使得它非常容易使用,因為它像 C 一樣將共享記憶體視為
一種檔案。由於此功能,這使得即使對於新手來說也很容易使用
功能。 最重要的是,SHMOP 使用 shm 段來儲存原始資料,
這意味著當您在
使用 C、perl 或其他程式設計語言開啟/建立/讀取/寫入 shm 段
這些段由 PHP 建立或將被 PHP 使用。 在這方面,它與
sysvshm 不同,後者的 shm 介面使用特殊的標頭,該標頭位於
共享記憶體段中,當您
想要從外部程式存取 php shm 時,這會增加不必要的難度。
此外,根據我在 Linux 2.2/2.4 和 FreeBSD 3.3 中的個人測試,SHMOP 大約
比 sysvshm 快 20%,主要是因為它不需要解析
特殊的標頭並以原始形式儲存資料。
儘管讀取和寫入共享記憶體不是原子性的,但讀寫僅一個位元組始終是原子性的。 如果您的應用程式經常讀取而很少寫入「小」資料塊(約 10-15 個位元組),這可能會非常有用。 您可以通過使用 8 位元校驗和(如 CRC-8)簽署您的資料來避免使用任何鎖定。 這是一種有效且可靠的方法,可確保您的資料未損壞。 冗餘自然是 8 位元。
您需要了解的是,sysvshm 在其能力方面極度以 php 為中心,它是一個相當粗糙的介面,可以將其他非 PHP 實用程式與之介接。例如,您是否嘗試過使用 sysvshm 來讀取 NOT 由 php 建立的 shm 段?這是不可行的,因為 sysvshm 使用專有格式,本質上它只能在 PHP 中使用,除非您花時間找出這種格式。
因此,基本上,shmop 的目的是提供一個簡單的共享記憶體介面,該介面可以與其他非 php shm 建立者一起使用。
希望這能澄清它。