Π‘Π°Π·ΠΎΠ²ΠΎΠ΅ использованиС

ΠŸΡ€ΠΈΠΌΠ΅Ρ€ #1 ΠžΠ±Π·ΠΎΡ€ ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΉ с раздСляСмой ΠΏΠ°ΠΌΡΡ‚ΡŒΡŽ

<?php

// Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ Π±Π»ΠΎΠΊΠ° с ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ΠΎΠΌ 0xff3 ΠΈ Ρ€Π°Π·ΠΌΠ΅Ρ€ΠΎΠΌ Π² 100 Π±Π°ΠΉΡ‚.
$shm_id = shmop_open(0xff3, "c", 0644, 100);
if (!
$shm_id) {
echo
"НСвозмоТно Π·Π°Ρ€Π΅Π·Π΅Ρ€Π²ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ Π±Π»ΠΎΠΊ Π² сСгмСнтС памяти\n";
}

// ΠŸΠΎΠ»ΡƒΡ‡Π΅Π½ΠΈΠ΅ Ρ€Π°Π·ΠΌΠ΅Ρ€Π° Π±Π»ΠΎΠΊΠ° Π² раздСляСмой памяти
$shm_size = shmop_size($shm_id);
echo
"Участок памяти, Ρ€Π°Π·ΠΌΠ΅Ρ€ΠΎΠΌ: " . $shm_size . ", ΡƒΡΠΏΠ΅ΡˆΠ½ΠΎ Π·Π°Ρ€Π΅Π·Π΅Ρ€Π²ΠΈΡ€ΠΎΠ²Π°Π½.\n";

// ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΎΡ‡Π½Π°Ρ запись Π½Π΅ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΉ строки Π² Ρ€Π°Π·Π΄Π΅Π»ΡΠ΅ΠΌΡƒΡŽ ΠΏΠ°ΠΌΡΡ‚ΡŒ
$shm_bytes_written = shmop_write($shm_id, "Мой малСнький блок", 0);
if (
$shm_bytes_written != strlen("Мой малСнький блок")) {
echo
"НС ΡƒΠ΄Π°Π»ΠΎΡΡŒ Π·Π°ΠΏΠΈΡΠ°Ρ‚ΡŒ вСсь Ρ€Π°Π·ΠΌΠ΅Ρ€ Π΄Π°Π½Π½Ρ‹Ρ…\n";
}

// ΠŸΠΎΠ»ΡƒΡ‡Π΅Π½ΠΈΠ΅ Ρ…Ρ€Π°Π½ΠΈΠΌΠΎΠΉ строки ΠΈΠ· раздСляСмой памяти
$my_string = shmop_read($shm_id, 0, $shm_size);
if (!
$my_string) {
echo
"НСвозмоТно ΠΏΡ€ΠΎΡ‡ΠΈΡ‚Π°Ρ‚ΡŒ Π΄Π°Π½Π½Ρ‹Π΅ ΠΈΠ· Π±Π»ΠΎΠΊΠ° памяти\n";
}
echo
"Π”Π°Π½Π½Ρ‹Π΅, Ρ€Π°Π·ΠΌΠ΅Ρ‰Ρ‘Π½Π½Ρ‹Π΅ Π² раздСляСмой памяти: " . $my_string . "\n";

// Π£Π΄Π°Π»Π΅Π½ΠΈΠ΅ Π±Π»ΠΎΠΊΠ° ΠΈ Π·Π°ΠΊΡ€Ρ‹Ρ‚ΠΈΠ΅ сСгмСнта раздСляСмой памяти
if (!shmop_delete($shm_id)) {
echo
"НСвозмоТно ΠΎΡ‚ΠΌΠ΅Ρ‚ΠΈΡ‚ΡŒ участок памяти для освобоТдСния.";
}
shmop_close($shm_id);

?>
οΌ‹Π”ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ

ΠŸΡ€ΠΈΠΌΠ΅Ρ‡Π°Π½ΠΈΡ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Π΅ΠΉ 1 note

up
3
Anonymous ΒΆ
8 years ago
Simple class for SHMOP implementation.

# Warning #

You can write/read only one direction. If you try read and write at the same time you get corrupt data. (Its works like STD in/out)
If you need bi-directional communication create 2 instance of shmop class.
Don't try attach more than one readers or writers for some shmid, you get corrupt data. This class use kludged spinlocks for improve speed, and not real atomic operations. You can add semaphore with flock, but it very slow. (~x3)

Benchmark:

Reads per sec: 6316 Data size per sec: 6.17 gb

<?php
########################
# shmopwriter.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;
}

########################

########################
# shmopreader.php
########################

$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];
}
########################

########################
# shmop.class.php
########################

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()
    {
        // Check SpinLock
        if(shmop_read($this->shmId, 0, 1) === "\0")
            return false;
        
        // Get Header
        $dataSize = (int)shmop_read($this->shmId, 1, $this->shmHeaderSize);

        $data = shmop_read($this->shmId, $this->shmHeaderOffset, $dataSize);
        // release spinlock
        shmop_write($this->shmId, "\0", 0);
        return $data;
    }
    
    public function write(string $data)
    {
        // Check SpinLock
        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);
        
        // pack very slow use kludge
        $dataSize .= "\0";

        // Write Header
        shmop_write($this->shmId, $dataSize, 1);
        // Write Data
        shmop_write($this->shmId, $data, $this->shmHeaderOffset);
        // Write spinlock
        shmop_write($this->shmId, "\1", 0);
        return true;
    }

    public function eof()
    {
        return (shmop_read($this->shmId, 0, 1) === "\2") ? true : false;
    }
    
    public function sendeof()
    {
        // Check SpinLock
        if(shmop_read($this->shmId, 0, 1) !== "\0")
            return false;

        shmop_write($this->shmId, "\2", 0);
        return true;
    }
    
    public function canWrite()
    {
        // Check SpinLock
        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);
    }
}

?>