4

什么时候出现这种情况?

如果您使用共享内存和信号量进行进程间锁定(使用pcntl 扩展),您应该关心信号量和共享内存段生命周期。例如,您编写后台工作者应用程序并使用主进程和一些子(分叉)进程进行作业处理。在它们之间使用共享内存和信号量是 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 系统对共享内存计数的信号量都有限制。如果您的应用程序应该创建和删除信号量的许多共享内存段,那么您不能等待它在进程关闭时自动释放。

问题

使用с您可以使用shmctlIPC_RMID- 它标记要删除的段。当当前附加到该段的最后一个进程已正确分离它时,实际删除本身就会发生。当然,如果当前没有进程附加到该段,则删除似乎是立即的。它就像简单的参考计数器一样工作。但是 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. 在这种情况下应该使用什么策略?只在主进程中销毁?其他情况?
4

1 回答 1

1

我检查了当前的PHP源代码IPC_RMID没有使用。然而,PHP 使用semop()它,SEM_UNDO标志,以防万一auto_release(见PHP sem_get() 手册)被设置。但请注意,这适用于每个进程级别。因此,如果您使用 PHP 作为 Apache 模块、FCGI 或 FPM,它可能无法按预期工作。不过,它应该适用于 CLI。

对于您的清理,这取决于“主”是否最后终止。

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

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
        }
    }
}

但请注意,在某些情况下不会执行析构函数

于 2014-06-09T14:33:39.093 回答