在单独的一个PHP进程中读写、建立、删除共享内存方面上你应该没有问题了。可是实际运行中不可能只是一个PHP进程在运行中。若是在多个进程的状况下你仍是沿用单个进程的处理方法,你必定会碰到问题--著名的并行和互斥问题。好比说有2个进程同时须要对同一段内存进行读写。当两个进程同时执行写入操做时,你将获得一个错误的数据,由于该段内存将之多是最后执行的进程的内容,甚至是由2个进程写入的数据轮流随机出现的一段混合的四不象。这显然是不能接受的。为了解决这个问题,咱们必须引入互斥机制。互斥机制在不少操做系统的教材上都有专门讲述,这里很少重复。实现互斥机制的最简单办法就是使用信号灯。信号量是另一种进程间(IPC)的方式,它同其余IPC机构(管道、FIFO、消息队列)不一样。php
说到信号量可能你们都很陌生,做为php确定知道mysql、redis中的锁,固然还有php文件锁。说白了就是锁,用来解决进程(线程同步的问题),访问前获取锁(获取不到则等待),访问后释放锁。html
信号量的做用就是,考虑是否有多个进程同时写入数据到共享内存的状况,是否须要避免冲突。mysql
举一个生活中的例子:以一个停车场的运做为例。简单起见,假设停车场只有三个车位,一开始三个车位都是空的。这时若是同时来了五辆车,看门人容许其中三辆直接进入,而后放下车拦,剩下的车则必须在入口等待,此后来的车也都不得不在入口处等待。这时,有一辆车离开停车场,看门人得知后,打开车拦,放入外面的一辆进去,若是又离开两辆,则又能够放入两辆,如此往复。在这个停车场系统中,车位是公共资源,每辆车比如一个线程,看门人起的就是信号量的做用。nginx
记得给环境开启两个扩展【enable-shmop --enable-sysvsem】redis
由于php默认不支持这些函数,因此须要重编译php。如要使用:
System V信号量,编译时加上 –enable-sysvsem
System V共享内存,编译时加上 –enable-sysvshm
System V消息队列,编译时加上 –enable-sysvmsg
Shared Memory,编译时加上 –enable-shmopsql
信号量系列函数函数
<?php //一、建立信号量惟一标识符 $key = 0x4337b101; //二、建立信号量资源ID $sem_resouce_id = sem_get($key); //三、接受信号量 sem_acqure($sem_resource_id); //四、释放信号量 sem_release($sem_resource_id); //五、销毁信号量 sem_remove($sem_resource_id);
简单小案例ui
<?php $key = 0x4337b101; $sem_id = sem_get($key); //请求信号控制权 if (sem_acquire($sem_id)) { $shm_id = shmop_open($key, 'c', 0644, 1024); //读取并写入数据 $count = (int) shmop_read($shm_id, 0, 8) + 1; shmop_write($shm_id, str_pad($count, 8, '0', STR_PAD_LEFT), 0); // echo shmop_read($shm_id, 0, 8); //关闭内存块 shmop_close($shm_id); //释放信号 sem_release($sem_id); }
若是出现报错:Warning: sem_release(): SysV semaphore 140680297324568 (key 0x4337b101) is not currently acquired in /usr/local/nginx/html/index.php on line 38spa
那是由于没有得到锁~操作系统
在Linux下命令观察,查看系统共享内存,信号量,队列
# ipcs
# ipcs -s //单独查看信号量的话,使用ipcs -s命令
稍微复杂的案例
<?php //建立共享内存区域 $shm_key = ftok(__FILE__, 'a'); $shm_id = shm_attach($shm_key, 1024, 0755); //var_dump($shm_id);die(); resource(4) of type (sysvshm) const SHARE_KEY = 1; $child_list = []; //加入信号量 $sem_id = ftok(__FILE__, 'b'); $signal = sem_get($sem_id); //$signal resource(5) of type (sysvsem) for ($i = 0; $i < 3; $i++) { $pid = pcntl_fork(); if ($pid == -1) { exit("Fork fail!".PHP_EOL); } elseif ($pid == 0) { //获取信号量 sem_acquire($signal); if (shm_has_var($shm_id,SHARE_KEY)) { $count = shm_get_var($shm_id, SHARE_KEY); $count++; //模拟业务处理 $sec = rand(1, 3); sleep($sec); shm_put_var($shm_id, SHARE_KEY, $count); } else { $count = 0; $sec = rand(1, 3); sleep($sec); shm_put_var($shm_id, SHARE_KEY, $count); } echo "child process: ".getmypid()." is writing! now count is: $count ".PHP_EOL; //释放信号量 sem_release($signal); exit("child process".getmypid()."end".PHP_EOL); } else { $child_list[] = $pid; } } while (count($child_list) > 0) { foreach ($child_list as $key => $pid) { $status = pcntl_waitpid($pid, $status); if ($status > 0 || $status == -1) { unset($child_list[$key]); } } sleep(1); } $count = shm_get_var($shm_id, SHARE_KEY); echo " $count ".PHP_EOL; //销毁信号量 sem_remove($signal); shm_remove($shm_id); shm_detach($shm_id);
实际运用中根据场景灵活运用就能够了~