PHP 共享内存使用与信号控制

共享内存

共享内存的使用主要是为了可以在同一台机器不一样的进程中共享一些数据,好比在多个 php-fpm 进程中共享当前进程的使用状况。这种通讯也称为进程间通讯(Inter-Process Communication),简称 IPC。php

PHP 内置的 shmop 扩展 (Shared Memory Operations) 提供了一系列共享内存操做的函数(多是用的人很少吧,这一起的文档尚未中文翻译)。在 Linux 上,这些函数直接是经过调用 shm* 系列的函数实现,而 Winodows 上也经过对系统函数的封装实现了一样的调用。html

主要函数:node

与此相关的还有一个很重要的函数:ftok,经过文件的 inode 信息(*nix 上经过 statls -i 命令查看)建立 IPC 的惟一 key(文件/文件夹的 inode 是惟一的)。这个函数在 Linux 上也是直接调用同名的系统函数实现,Windows 上仍是使用一些封装。安全

一个简单的计数例子:ide

<?php # 建立一块共享内存 $shm_key = ftok(__FILE__, 't'); $shm_id = shmop_open($shm_key, 'c', 0644, 8); # 读取并写入数据 $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); # 关闭内存块,并不会删除共享内存,只是清除 PHP 的资源 shmop_close($shm_id); 

以上这段代码没执行一次计数加 1,并且数据是在不一样进程之间共享的。也就是说除非手动删除这块内存使用,不然这个数据是不会重置的。函数

有个须要稍微注意的点:shmop_open 的第二个参数是个 flag,相似 fopen 的第二个参数,其取值有之前几个:php-fpm

  • “a” 只读访问;
  • “c” 若是内存片断不存在,则建立,若是存在,则可读写;
  • “w” 读写;
  • “n” 建立新的内存片断,若是一样 key 的已存在,则会建立失败,这是为了安全使用共享内存考虑。

此外,因为使用的共享内存片断是固定长度的,在存储和读取的时候要计算好数据的长度,否则可能会写入失败或者读取空值。测试

信号控制

既然上面使用到了共享内存存储数据,就须要考虑是否有多个进程同时写入数据到共享内存的状况,是否须要避免冲突。若是是这样,就须要引入信号量进行控制。ui

PHP 也提供了相似的内置扩展 sysvsem(这个扩展在 Windows 环境下没有,文档中将 ftok 函数也归到这个扩展中,但实际上 ftok 是在标准函数库中提供的,因此在 Windows 下也是可用的)。this

在说信号量控制以前,先说另一件有意思的事情:看官方文档你会发现这里一样也有共享内存操做的函数(shm_*),由于这实际上是同一类别(或者说来自于同一做者)的三个扩展,还有一个是 sysvmsg(队列消息) 。函数的实现上稍有差异,但实际作的事情基本相同。这和上文的 shmop 扩展有什么区别呢?shmop 源码下的 README 文件有简单的说明:

PHP already had a shared memory extension (sysvshm) written by Christian Cartus cartus@atrior.de, unfortunately this extension was designed with PHP only in mind and offers high level features which are extremely bothersome for basic SHM we had in mind.

简单说来:sysvshm 扩展提供的方法并非原封不动的存储用户的数据,而是先使用 PHP 的变量序列化函数对参数进行序列化而后再进行存储。这就致使经过这些方法存储的数据没法和非 PHP 进程共享。不过这样也能存储更丰富的 PHP 数据类型,上文的扩展中 shmop_write 只能写入字符串。那么为何 sysvshm 一样不支持 Windows 呢?由于其并无引入封装了 shm* 系列函数的 tsrm_win32.h 的头文件。

引入信号控制以后的示例:

<?php $id_key = ftok(__FILE__, 't'); $sem_id = sem_get($id_key); # 请求信号控制权 if (sem_acquire($sem_id)) { $shm_id = shmop_open($id_key, 'c', 0644, 8); # 读取并写入数据 $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); } 

可是本地想模拟实现写入冲突其实是很是难的(考虑到计算机的执行速度)。在本地测试中,使用 for 循环操做时若是不使用 shmop_close 关闭资源会出现没法打开共享内存的错误警告。这应该是由于正在共享内存被上一次操做占用中尚未释放致使。

相关文章
相关标签/搜索