为什么PHP's sem_acquire阻塞程序执行


Why does PHP's sem_acquire blocks program execution?

我正在一个非常大而复杂的PHP项目上工作,该项目运行在gentoo Linux上,显然有一些PHP信号量的问题。由于项目的规模和复杂性,我不能发布代码。我也无法提供重现该问题的工作示例。它可能是由程序的复杂性以一种不确定的方式引起的。

问题是:PHP代码试图使用信号量向共享内存写入和读取。在产生问题的情况下,执行以下操作:

  1. 在006.68时刻,PHP 4.4.9执行以下代码,将5字节的数据写入共享内存,其中$iVarKey的值为2010147023

    sem_acquire($this->rSemaphore);
    shm_put_var($this->rShm, $iVarKey, $mVar);
    sem_release($this->rSemaphore);
    

    此动作在时间006.69结束

  2. 在006.77时刻,PHP 5.2.10执行以下代码从共享内存中读取5字节的数据,其中$iVarKey的值为622679600:

    sem_acquire($this->rSemaphore);
    $mVar = shm_get_var($this->rShm,$iVarKey);
    sem_release($this->rSemaphore);
    

    此操作在时间006.78结束

  3. 在时间016.01执行以下代码由PHP 5.2.10(与#2相同的代码行)从共享内存中读取5字节的数据,$iVarKey的值为2010147023(与#1相同):

    sem_acquire($this->rSemaphore);
    $mVar = shm_get_var($this->rShm,$iVarKey);
    sem_release($this->rSemaphore);
    

    此操作现在大约需要2分钟,尽管具有相同$iVarKey的资源/信号量已经在大约10秒前释放。在此期间没有访问共享内存,因为我已经确定了对sem_acquire的每次调用!

sem_acquire怎么可能阻塞程序的执行,尽管它不应该阻塞。也许是4.4.9/5.2.10版本的bug ?有人看到过类似的东西吗?有解决办法吗?我能做些什么来进一步探讨这个问题吗?

在这个问题上我将非常感谢你的帮助!

评论:

    如果你需要更多的信息,我会尽量提供给他们
  • 请不要评论PHP4或并行使用两个PHP版本。
  • 如果有人认为这个问题不属于这里,请提供指导。

附加信息:-我检查了sem_release的每个调用,似乎没有一个返回FALSE。因此,我的问题不是由失败的发布引起的。—当系统阻塞时,ipcs -s返回以下输出,与系统未阻塞时相同

    ------ Semaphore Arrays --------
    key        semid      owner      perms      nsems     
    0x000f4240 0          root      666        3         
    0x00000001 32769      root      666        3         
    0x00000000 65538      apache    600        1 
  • 阻塞信号量对sem_get()的调用是

    $this->rSemaphore = sem_get(1000000,1,0666,1);
    
  • 唯一涉及ftok的呼叫似乎永远不会被调用。

您的代码假设总是获取信号量,这可能不是真的。试试这个

if(sem_acquire($this->rSemaphore)) {
    $mVar = shm_get_var($this->rShm,$iVarKey);
    if(!sem_release($this->rSemaphore)) {
      //log error
    }
}
else {
    //log error
}

一个潜在的解决方法是使用flock()代替信号量函数。缺点是flock()较慢,但根据您的使用情况,它可能足够快

$file = "/tmp/my_semaphore";
$fp = fopen($file,"r+");
if(flock($fp, LOCK_EX)) { // wait/acquire lock
    shm_put_var($this->rShm, $iVarKey, $mVar);
    if(!flock($fp, LOCK_UN)) { //release file lock
      //log error
    }
}
else {
    //something went wrong, unable to attain lock
}