用於 SHMOP 實作的簡單類別。
# 警告 #
您只能單向讀寫。如果您嘗試同時讀寫,您將會得到損毀的資料。(它的運作方式類似標準輸入/輸出)
如果您需要雙向通訊,請建立兩個 shmop 類別的實例。
對於同一個 shmid,請勿嘗試附加多個讀取器或寫入器,否則會造成資料損毀。此類別使用了一些權宜的 spinlocks 來提升速度,並未使用真正的原子操作。您可以使用 flock 加入信號量,但速度會非常慢(約為原本的 1/3)。
效能基準測試
每秒讀取次數:6316 每秒資料大小:6.17 GB
<?php
$blockSize = 1024 * 1024 * 100;
$data = random_bytes($blockSize);
try
{
$shm = new SHMOP('shmopwriter.php', 'c', 644, $blockSize);
while(1)
{
if(!$shm->canWrite())
continue;
$shm->write($data);
}
$shm->close();
} catch (Exception $e) {
echo 'Error: ', $e->getMessage(), PHP_EOL;
exit;
}
$blockSize = 1024 * 1024 * 100;
$shm = new SHMOP('shmopwriter.php', 'c', 644, $blockSize);
$readsMT = 0;
$readsPS = 0;
while(!$shm->eof())
{
$times = microtime(true);
if(($data = $shm->read()) === false)
continue;
$readsPS++;
$readsMT += round(((microtime(true) - $times ) * 1000), 3);
if($readsMT > 1000)
{
echo 'Reads per sec: ', $readsPS, ' Data size per sec: ', convert($blockSize * $readsPS), PHP_EOL;
$readsPS = 0;
$readsMT = 0;
}
}
function convert($size)
{
$unit=array('b','kb','mb','gb','tb','pb');
return @round($size/pow(1024,($i=floor(log($size,1024)))),2).' '.$unit[$i];
}
class SHMOP
{
private $shmId;
private $shmHeaderSize;
private $shmHeaderOffset;
private $shmBlockSize;
private $shmMaxBlockSize;
function __construct(string $pathname, string $flags, int $mode, int $size)
{
$this->shmHeaderSize = strlen($size);
$this->shmHeaderOffset = $this->shmHeaderSize + 1;
$this->shmMaxBlockSize = $size;
$this->shmBlockSize = $size + $this->shmHeaderOffset + 1;
$this->shmId = shmop_open(ftok($pathname, 's'), $flags, $mode, $this->shmBlockSize);
if(!$this->shmId)
throw new Exception('shmop_open error');
}
function __destruct()
{
if(!$this->shmId)
return;
$this->close();
}
public function read()
{
if(shmop_read($this->shmId, 0, 1) === "\0")
return false;
$dataSize = (int)shmop_read($this->shmId, 1, $this->shmHeaderSize);
$data = shmop_read($this->shmId, $this->shmHeaderOffset, $dataSize);
shmop_write($this->shmId, "\0", 0);
return $data;
}
public function write(string $data)
{
if(shmop_read($this->shmId, 0, 1) !== "\0")
return false;
$dataSize = strlen($data);
if($dataSize < 1)
throw new Exception('dataSize < 1');
if($dataSize > $this->shmMaxBlockSize)
throw new Exception('dataSize > shmMaxBlockSize: '. $this->shmMaxBlockSize);
$dataSize .= "\0";
shmop_write($this->shmId, $dataSize, 1);
shmop_write($this->shmId, $data, $this->shmHeaderOffset);
shmop_write($this->shmId, "\1", 0);
return true;
}
public function eof()
{
return (shmop_read($this->shmId, 0, 1) === "\2") ? true : false;
}
public function sendeof()
{
if(shmop_read($this->shmId, 0, 1) !== "\0")
return false;
shmop_write($this->shmId, "\2", 0);
return true;
}
public function canWrite()
{
return (shmop_read($this->shmId, 0, 1) === "\0") ? true : false;
}
public function close()
{
return @shmop_close($this->shmId);
}
private function delete()
{
$del = @shmop_delete($this->shmId);
if($del === false)
return false;
return @shmop_close($this->shmId);
}
}
?>