作为phper既然了解共享内存函数shmop的使用方法,那么就必须要了解一下信号量是什么,以及信号量使用的代码案例详解编程语言

在单独的一个PHP进程中读写、创建、删除共享内存方面上你应该没有问题了。但是实际运行中不可能只是一个PHP进程在运行中。如果在多个进程的情况下你还是沿用单个进程的处理方法,你一定会碰到问题–著名的并行和互斥问题。比如说有2个进程同时需要对同一段内存进行读写。当两个进程同时执行写入操作时,你将得到一个错误的数据,因为该段内存将之可能是最后执行的进程的内容,甚至是由2个进程写入的数据轮流随机出现的一段混合的四不象。这显然是不能接受的。为了解决这个问题,我们必须引入互斥机制。互斥机制在很多操作系统的教材上都有专门讲述,这里不多重复。实现互斥机制的最简单办法就是使用信号灯。信号量是另外一种进程间(IPC)的方式,它同其他IPC机构(管道、FIFO、消息队列)不同。

说到信号量可能大家都很陌生,作为php肯定知道mysql、redis中的锁,当然还有php文件锁。说白了就是锁,用来解决进程(线程同步的问题),访问前获取锁(获取不到则等待),访问后释放锁。

信号量的作用就是,考虑是否有多个进程同时写入数据到共享内存的情况,是否需要避免冲突。

举一个生活中的例子:以一个停车场的运作为例。简单起见,假设停车场只有三个车位,一开始三个车位都是空的。这时如果同时来了五辆车,看门人允许其中三辆直接进入,然后放下车拦,剩下的车则必须在入口等待,此后来的车也都不得不在入口处等待。这时,有一辆车离开停车场,看门人得知后,打开车拦,放入外面的一辆进去,如果又离开两辆,则又可以放入两辆,如此往复。在这个停车场系统中,车位是公共资源,每辆车好比一个线程,看门人起的就是信号量的作用。

记得给环境开启两个扩展【enable-shmop –enable-sysvsem】

 

因为php默认不支持这些函数,所以需要重编译php。如要使用:
System V信号量,编译时加上 –enable-sysvsem
System V共享内存,编译时加上 –enable-sysvshm
System V消息队列,编译时加上 –enable-sysvmsg
Shared Memory,编译时加上 –enable-shmop

 

作为phper既然了解共享内存函数shmop的使用方法,那么就必须要了解一下信号量是什么,以及信号量使用的代码案例详解编程语言

作为phper既然了解共享内存函数shmop的使用方法,那么就必须要了解一下信号量是什么,以及信号量使用的代码案例详解编程语言

 

信号量系列函数

<?php 
//1、创建信号量唯一标识符 
$key = 0x4337b101; 
//2、创建信号量资源ID 
$sem_resouce_id = sem_get($key); 
//3、接受信号量 
sem_acqure($sem_resource_id); 
//4、释放信号量 
sem_release($sem_resource_id); 
//5、销毁信号量 
sem_remove($sem_resource_id);

 简单小案例

<?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 38

那是因为没有获得锁~

在Linux下命令观察,查看系统共享内存,信号量,队列

# ipcs

作为phper既然了解共享内存函数shmop的使用方法,那么就必须要了解一下信号量是什么,以及信号量使用的代码案例详解编程语言

# 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);

实际运用中根据场景灵活运用就可以了~

原创文章,作者:奋斗,如若转载,请注明出处:https://blog.ytso.com/17182.html

(0)
上一篇 2021年7月19日
下一篇 2021年7月19日

相关推荐

发表回复

登录后才能评论