正确的策略与共享内存和信号量删除RAII像PHP类

时间:2014-05-06 06:48:18

标签: php linux semaphore shared-memory pcntl

何时出现这种情况?

如果您使用共享内存和信号量进行interpocess锁定(使用pcntl extension),您应该关心信号量和共享内存段生命周期。例如,您编写backgroud worker应用程序并使用master和some child(forked)进程进行作业处理。使用共享内存和信号量在它们之间进行IPC的好主意。而且RAII喜欢shm_xxx和sem_xxx php函数的类包装看起来也是个好主意。

示例

class Semaphore
{
     private $file;

     private $sem;

     public function __construct()
     {
        $this->file = tempnam(sys_get_temp_dir(), 's');
        $semKey = ftok($this->file, 'a');

        $this->sem = sem_get($semKey, 1); //auto_release = 1 by default
     }

     public function __destruct()
     {
         if (is_resource($this->sem) {
            sem_remove($this->sem);
         }
     }

     ....
}

不是很好的选择 - 在分叉之后我们在父母和子进程中有一个实例。其中任何一个的析构函数都会破坏信号量。

为什么重要

大多数Linux系统都限制共享内存计数的信号量。如果你有应用程序应该创建和删除信号量的许多共享内存段,你不能等待它在进程关闭时自动释放。

问题

使用с您可以IPC_RMID使用shmctl - 它标记要删除的细分。当当前附加到段的最后一个进程已正确分离它时,会发生实际的删除。当然,如果当前没有任何进程附加到该段,则立即删除。它的工作原理就像简单的参考计数器。但是php没有实现shmctl

另一种策略 - 仅在主进程的析构函数中销毁信号量:

class Semaphore
{
     ... 
     private $pid;

     public function __construct()
     {
        $this->pid = getmypid();
        ...
     }

     public function __destruct()
     {
         if (is_resource($this->sem) && $this->pid === getmypid()) {
            sem_remove($this->sem);
         }
     }
     ....
}

所以,问题是

  1. 如果有任何方法在php中使用IPC_RMID?
  2. 在这种情况下应采用什么策略?仅在主进程中销毁?其他情况?

1 个答案:

答案 0 :(得分:1)

我检查了当前PHP source code并且未使用IPC_RMID。但是,PHP使用semop()并使用SEM_UNDO标志,以防auto_release(请参阅PHP sem_get() manual)设置。但请注意,这适用于每个进程级别。因此,如果您使用PHP作为Apache模块,或FCGI或FPM,它可能无法按预期工作。不过,它应该可以很好地用于CLI。

对于你的清理,它取决于“master”是否最后终止。

如果您不知道,可以自己实施引用计数。

class Semaphore
{
    static private $m_referenceCount = 0;

    public function __construct()
    {
        ++self::$m_referenceCount;
        // aquire semaphore
    }

    public function __destruct()
    {
        if (--self::$m_referenceCount <= 0) {
            // clean up
        }
    }
}

但请注意destructor is NOT executed in some circuumstances