有时我们在处理并发操作时会使用信号量做进程同步,如下一个php应用的例子
$sem_id = sem_get($id,1,0666,true);
sem_acquire($sem_id);
...
sem_release($sem_id);
我们release了资源,但是系统没有真正的释放资源 通过 [root@localhost ~]# ipcs -s
------ Semaphore Arrays -------- key semid owner perms nsems 0x0002152f 32769 www 666 3 0x00019fff 65538 www 666 3 0x00019ff1 98307 www 666 3 ....
我们知道 ,Semaphores 会很快达到上限
#ipcs -l
max semaphores per array = 250
系统默认是250个,于是当我们再次通过sem_get()获取资源句柄时就会得到以下错误: Warning: sem_get() [function.sem-get]: failed for key 0x(n): No space left on device in ...
最后修改程序,加入 sem_remove($sem_id);
新的问题出现了 sem_release() [function.sem-release]: failed to release key 0xxxxxxx: Invalid argument in...
这是由于出现竞争,假设a进程等待b进程,b完成时执行了sem_remove,所以a进程执行sem_release就会出错,因为信号里已经被释放了,故提示信号量无效.
所以php下的信号量不适用于需要创建大量不同锁的环境. 折衷的方法就是将锁hash到一个范围:
如: $sem_id = sem_get($id % 100,1,0666,true); ...
另外记得信号量上限的配置,这里是调整到2048 sysctl -w kernel.sem="1024 32000 100 2048" echo "kernel.sem=1024 32000 100 2048" >> /etc/sysctl.conf
最后想提一个非常重要的问题,当sem_get第4个参数auto_release为true时,php的信号里的作用范围只限当前区间,如:
function lock() { $sem_id = sem_get($id,1,0666,true); sem_acquire($sem_id); ... }
lock (); //函数中调用了信号量,作用域只在函数内 lock(); // 再次调用不会死锁,因为第一个信号量锁定作用已消失
移除: for whatever in `ipcs -s | awk '{print $2}'`; do echo $whatever; done