SHMOP 实现的简单类。
# 警告 #
您只能写/读一个方向。如果您尝试同时读写,您将获得损坏的数据。 (它像 STD 输入/输出一样工作)
如果您需要双向通信,请创建 2 个 shmop 类实例。
不要尝试为某个 shmid 附加多个阅读器或写入器,您将获得损坏的数据。此类使用笨拙的自旋锁来提高速度,而不是真正的原子操作。您可以使用 flock 添加信号量,但它非常慢。 (~x3)
基准测试
每秒读取次数: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);
}
}
?>