如何使用 PHP shmop 创建和操作共享内存段,使用它们存储可供其他应用程序使用的数据。

1. 创建内存段

共享内存函数类似于文件操作函数,但无需处理一个流,您将处理一个共享内存访问 ID。第一个示例就是 shmopopen 函数,它允许您打开一个现有的内存段或创建一个新内存段。此函数非常类似于经典的 fopen 函数,后者打开用于文件操作的流,返回一个资源供其他希望读取或写入该打开的流的函数使用。让我们看看 shmopopen的用法:

<?php
$key = ftok(__FILE__, 'h');
$mode = 'c';
$permissions = 0644;
$size = 1024;
$shmid = shmop_open($key, $mode, $permissions, $size);
?>

第一个参数($key):

系统建立IPC通讯 (消息队列、信号量和共享内存) 时必须指定一个key值。通常情况下,该key值通过ftok函数得到, * *key是一个我们逻辑上表示共享内存段的标识。不同进程只要选择同一个Key值就可以共享同一段存储段。

第二个参数($mode):

访问模式,它类似于fopen的访问模式,有以下几种

  • 模式 “a”,它允许您访问只读内存段
  • 模式 “w”,它允许您访问可读写的内存段
  • 模式 “c”,它创建一个新内存段,或者如果该内存段已存在,尝试打开它进行读写 *模式 “n”,它创建一个新内存段,如果该内存段已存在,则会失败,返回 false,并伴随有warning: unable to attach or create shared memory segment

第三个参数($permissions):

内存段的权限。您必须在这里提供一个八进制值,它类似于UNIX操作系统文件和目录的操作权限。

第四个参数($size):

内存段大小,以字节为单位。在写入一个内存段之前,您必须在它之上分配适当的字节数。

返回结果:

此函数返回一个 ID 编号,其他函数可使用该 ID 编号操作该共享内存段。这个 ID 是共享内存访问 ID,与系统 ID 不同,它以参数的形式传递。请注意不要混淆这两者。如果失败,shmop_open 将返回 FALSE。

shmop_open成功后,使用ipcs -m, 可以查看到刚刚创建的内存段,注意 申请的内存段有严格的权限,比如用root用户申请的,普通用户就无权访问

2. 向内存段写入数据

使用 shmop_write 函数向共享内存块写入数据。此函数的使用很简单,它仅接受 3 个参数,如下所示。

<?php
//这里shmid可以延用上一段代码返回的shmid
$shmid = shmop_open(ftok(__FILE__,'h'), 'c', 0644, 1024);
shmop_write($shmid, "Hello World!", 0); ?>

这个函数类似于 fwrite 函数, 在这里有三个参数。 * 第一个参数(shmid):是shmopopen返回的ID,它识别您操作的共享内存块。∗第二个参数(shmid):是shmopopen返回的ID,它识别您操作的共享内存块。∗第二个参数(data):是您希望存储的数据。 * 第三个参数($offset):是您希望开始写入的位置。默认情况下,我们始终使用 0 来表示开始写入的位置。

返回结果:此函数在失败时会返回 FALSE,在成功时会返回写入的字节数。

3. 从内存段读取数据

从共享内存段读取数据很简单。您只需要一个打开的内存段和 shmop_read 函数,它接受三个参数,如下所示:

<?php
$shmid = shmop_open(ftok(\__FILE_\_,'h'), 'c', 0644, 1024);
shmop_write($shmid, "Hello World\!", 0);
var_dump(shmop_read($shmid, 0, 11));
?>
  • 第一个参数($shmid):是 shmop_open 返回的 ID,它识别您操作的共享内存块。
  • 第二个参数($start):是您希望从内存段读取的位置,这个参数可以始终为0, 表示数据的开头
  • 第三个参数(count):是您希望读取的字节数。一般情况下我们用shmopsize(count):是您希望读取的字节数。一般情况下我们用shmopsize(shmid),以便完整的读取它。

4. 删除内存段

shmop_delete 该函数只接收一个参数,如下所示:

<?php
$shmid = shmop_open(ftok(\__FILE_\_,'h'), 'c', 0644, 1024);
shmop_delete($shmid);
?>

其实这个函数不会实际删除该内存段。它将该内存段标记为删除状态,因为共享内存段在有其他进程正在使用它时无法被删除。shmop_delete 函数将该内存段标记为删除,阻止任何其他进程打开它。要删除它,我们需要关闭该内存段。

5. 关闭内存段

打开一个共享内存段会 “附加” 到它。附加该内存段之后,我们可在其中进行读取和写入,但完成操作后,我们必须从它解除。

<?php
$shmid = shmop_open(ftok(\__FILE_\_,'h'), 'c', 0644, 1024);
shmop_write($shmid, "Hello World\!", 0);
shmop_delete($shmid); shmop_close($shmid);
?>

共享内存的原子操作 - 信号控制

针对共享内存的写操作本身不是原子性的,那么当我们大量并发进行读写的时候,怎么保证原子性呢,这里要引入信号量进行控制。

PHP 也提供了内置扩展 sysvsem ,其实我们在看sysvsem 提供的一系列sem*的方法的时候,就会想到,这和上面提到的shmop*有什么区别呢,我们来看官房文档中的这一个解释: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 扩展提供的方法在存储之前对用户的数据进行serialize处理,这里就导致这个存储的数据是无法与其它语言共享的,这一系列方法是php only的方法。

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

<?php
$key = ftok(_FILE_, 'h') $mode = "c";
$permissions = 0755;
$size = 1024; // 内存段的大小,单位是字节
$semid = sem_get($key); # 请求信号控制权
if (sem_acquire($semid)) {
$shmid = shmop_open($key, 'c', 0644, 1024); # 读取并写入数据
shmop_write($shmid, '13800138000', 0); # 关闭内存块
shmop_close($shmid); # 释放信号 sem_release($semid);
}

共享内存的操作是非常快的,在本地想要模拟实现写入冲突是非常困难的,但是本地想模拟实现写入冲突实际上是非常难的(考虑到计算机的执行速度)。在本地测试中,使用 for 循环操作时如果不使用shmop_close 关闭资源会出现无法打开共享内存的错误警告。这应该是因为正在共享内存被上一次操作占用中还没有释放导致。

PHP共享内存的更多相关文章

  1. Linux 共享内存详解一

    共享内存段被多个进程附加的时候,如果不是所有进程都已经调用shmdt,那么删除该共享内存段时,会出现一个临时的不完整的共享内存段(key值是0),无法彻底删除.只有当所有进程都调用shmdt,这个临时 ...

  2. PHP进程通信基础——信号量+共享内存通信

    PHP进程通信基础--信号量+共享内存通信 由于进程之间谁先执行并不确定,这取决于内核的进程调度算法,其中比较复杂.由此有可能多进程在相同的时间内同时访问共享内存,从而造成不可预料的错误.信号量这个名 ...

  3. C++ 共享内存 函数封装

    #pragma once #include <string> #include <wtypes.h> #include <map> using namespace ...

  4. Linux学习笔记(14)-进程通信|共享内存

    在Linux中,共享内存是允许两个不相关的进程访问同一个逻辑内存的进程间通信方法,是在两个正在运行的进程之间共享和传递数据的一种非常有效的方式. 不同进程之间共享的内存通常安排为同一段物理内存.进程可 ...

  5. linux 共享内存 shmat,shmget,shmdt,shmctl

    shmget int shmget(key_t key, size_t size, int flag);//开辟一段共享内存 key_t key :标识符的规则() size_t size :共享内存 ...

  6. Linux进程间通信(六):共享内存 shmget()、shmat()、shmdt()、shmctl()

    下面将讲解进程间通信的另一种方式,使用共享内存. 一.什么是共享内存 顾名思义,共享内存就是允许两个不相关的进程访问同一个逻辑内存.共享内存是在两个正在运行的进程之间共享和传递数据的一种非常有效的方式 ...

  7. linux后台查看共享内存和消息队列的命令

    ipcs ipcs -q : 显示所有的消息队列 ipcs -qt : 显示消息队列的创建时间,发送和接收最后一条消息的时间 ipcs -qp: 显示往消息队列中放消息和从消息队列中取消息的进程ID ...

  8. c++共享内存(转载)

    对于连个不同的进程之间的通信,共享内存是一种比较好的方式,一个进程把数据发送到共享内存中, 另一个进程可以读取改数据,简单记录一下代码 #define BUF_SIZE 256 TCHAR szNam ...

  9. OpenMP共享内存并行编程详解

    实验平台:win7, VS2010 1. 介绍 平行计算机可以简单分为共享内存和分布式内存,共享内存就是多个核心共享一个内存,目前的PC就是这类(不管是只有一个多核CPU还是可以插多个CPU,它们都有 ...

  10. android共享内存

    在android下不能通过shm_open使用共享内存. 网上有好多关于android下使用Ashmem实现共享内存的,但经过尝试该方法可以mmap出内存,但是和另一个进程没有实现共享. 具体的使用方 ...

随机推荐

  1. MySQL系列:innodb源码分析 图 ---zerok的专栏

    http://blog.csdn.net/yuanrxdu/article/details/40985363

  2. What you can talk

    data buffer who locked the account hash join cost memory and nested loop do not. How to make it hash ...

  3. Oracle Auto Increment Column - Sequence as Default Value

        Solution 1: Prior to Oracle 11g, sequence assignment to a number variable could be done through ...

  4. jsp param动作标签

    param 标签以"名字-值"对的形式为其它标签提供附加消息.这个标签与jsp:include.jsp:forward.jsp:plugin标签一起使用. param 动作标签 & ...

  5. leetcode第一刷_Same Tree

    回来更博客的时候才发现.这道题不是跟推断树是不是对称的很相像吗.这个也是用了两个指针同一时候递归啊,有时候思维的局限真可笑. class Solution { public: bool isSameT ...

  6. java js url传参中文乱码

    String item = this.getRequest().getParameter("item"); item = new String(item.getBytes(&quo ...

  7. 回调函数实现类似QT中信号机制(最简单)

    1. 定义回调接口类: class UIcallBack{public: virtual void onAppActivated() = 0; virtual void onShowMore() = ...

  8. 《TCP/IP具体解释》读书笔记(21章)-TCP的超时与重传

    TCP提供可靠的运输层. 它使用的方法之中的一个就是确认从还有一端收到的数据.但数据和确认都有可能会丢失.TCP通过在发送时设置一个定时器来解决这样的问题.假设当定时器溢出时还没有收到确认,它就重传该 ...

  9. Android EditText技巧总结

    一.默认不获取焦点: 在布局文件的父控件中,设置如下属性: android:focusable="true" android:focusableInTouchMode=" ...

  10. 包含utf8字符的 pickle 转 json的大坑处理过程

    背景:希望将pickle转换为json,由于pickle里有utf8的字符,因此转换失败. 转换代码如下: ''' Convert a pkl file into json file ''' impo ...