一个是shm,它其实是变量共享,会把对象变量序列化后再储存。使用起来却是挺方便,可是序列化存储对于效率优先的内存访问操做而言就没啥意义了。php
另一个是shmop,它是Linux和Windows通用的,不过功能上比shm弱了一些,在 Linux 上,这些函数直接是经过调用 shm* 系列的函数实现,而 Winodows 上也经过对系统函数的封装实现了一样的调用。linux
要建立共享内存段须要使用函数shmop,那么前提须要开启扩展。redis
shmop_open (建立或打开共享内存块)、shmop_write (向共享内存块中写入数据)、shmop_read (从共享内存块中读取数据)、shmop_size (获取共享内存块的大小)、shmop_close (关闭共享内存块)、shmop_delete (删除共享内存块)sql
<?php //建立一块共享内存 $shm_key = 0x4337b101; $shm_id = @shmop_open($shm_key, 'c', 0644, 1024); //读取并写入数据 $data = shmop_read($shm_id, 0, 1024); shmop_write($shm_id, json_encode($data), 0); $size = shmop_size($shm_id); //获取内存中实际数据占用大小 //关闭内存块,并不会删除共享内存,只是清除 PHP 的资源 shmop_close($shm_id);
该函数中出现的第一个事物是系统 ID 参数。这是标识系统中的共享内存段的数字。json
第二个参数是访问模式,它很是相似于 fopen 函数的访问模式。您能够在 4 种不一样的模式下访问一个内存段:数组
模式 “a”,它容许您访问只读内存段,只读访问
模式 “w”,它容许您访问可读写的内存段,读写
模式 “c”,它建立一个新内存段,或者若是该内存段已存在,尝试打开它进行读写
模式 “n”,它建立一个新内存段,若是一样 key 的已存在,则会建立失败,这是为了安全使用共享内存考虑。 缓存
第三个参数是内存段的权限。您必须在这里提供一个八进制值。安全
第四个参数提供内存段大小,以字节为单位。因为使用的共享内存片断是固定长度的,在存储和读取的时候要计算好数据的长度,否则可能会写入失败或者读取空值。。nosql
请注意,此函数返回一个 ID 编号,其余函数可以使用该 ID 编号操做该共享内存段。这个 ID 是共享内存访问 ID,与系统 ID 不一样,它以参数的形式传递。请注意不要混淆这二者。若是失败,shmop_open 将返回 FALSE。在建立内存块时建议key参数用常量而不用变量,不然颇有可能形成内存泄露。函数
这个函数相似于 fwrite 函数,后者有两个参数:打开的流资源(由 fopen 返回)和您但愿写入的数据。shmop_write 函数也执行此任务。
第一个参数是 shmop_open 返回的 ID,它识别您操做的共享内存块。第二个参数是您但愿存储的数据,最后的第三个参数是您但愿开始写入的位置。默认状况下,咱们始终使用 0 来表示开始写入的位置。请注意,此函数在失败时会返回 FALSE,在成功时会返回写入的字节数。
从共享内存段读取数据很简单。您只须要一个打开的内存段和 shmop_read 函数。此函数接受一些参数,工做原理相似于 fread。
请留意这里的参数。shmop_read 函数将接受 shmop_open 返回的 ID,咱们已知道它,不过它还接受另外两个参数。第二个参数是您但愿从内存段读取的位置,而第三个是您但愿读取的字节数。第二个参数能够始终为 0,表示数据的开头,但第三个参数可能存在问题,由于咱们不知道咱们但愿读取多少字节。
这很是相似于咱们在 fread 函数中的行为,该函数接受两个参数:打开的流资源(由 fopen 返回)和您但愿从该流读取的字节数。使用 filesize 函数(它返回一个文件中的字节数)来完整地读取它。
好比,咱们开辟了一个长度为100字节的内存空间,可是实际存入的数据长度仅仅90,那么使用shmop_size返回的值就是90.
该函数仅接受一个参数:咱们但愿删除的共享内存 ID,这不会实际删除该内存段。它将该内存段标记为删除,由于共享内存段在有其余进程正在使用它时没法被删除。shmop_delete 函数将该内存段标记为删除,阻止任何其余进程打开它。要删除它,咱们须要关闭该内存段。在建立内存块时建议key参数用常量而不用变量,不然颇有可能形成内存泄露。
shmop_close(关闭内存段)
咱们在对内存段进行读取和写入,但完成操做后,咱们必须从它解除,这很是相似于处理文件时的 fclose 函数。打开包含一个文件的流并在其中读取或写入数据后,咱们必须关闭它,不然将发生锁定。
简单测试结果查看
我是在LNMP环境下操做的,若是你也和我同样,在执行完简单的操做以后,可使用linux命令查看一下地址和占用大小
# ipcs -m
[root@bogon ~]# ipcs -m ------ Shared Memory Segments -------- key shmid owner perms bytes nattch status 0x00000000 0 gdm 600 393216 2 dest 0x00000000 32769 gdm 600 393216 2 dest 0x4337b101 884750 nobody 644 1024 0
命令说明
key :共享内存的惟一的key值,共享内存经过该key来判断你读取的是哪一块内存。
shmid:当使用key来获取内存时,你得到的是这个id的值。它做为你操做内存块的标识。
owner:建立该共享内存块的用户
perms:该共享内存的读写权限,8禁止,能够是777,与文件的读写权限一致。
bytes:该内存块的大小
nattch:链接该内存块的进程数
status:当前状态,如:dest,即将删除等。
项目实际应用小案例
/** * 将领技能 */ class Generalskill_model extends CI_Model { private $_memory_key = 0x4337b001; //共享内存地址key private $_memory_size = 1048576; //开辟共享内存大小 //最好根据实际数据长度大小定义。 public function __construct() { parent::__construct(); } public function get_skill_list() { $data = []; $shmid = @shmop_open($this->_memory_key, 'a', 0644, $this->_memory_size); if ($shmid === FALSE) { $shmid = @shmop_open($this->_memory_key, 'c', 0644, $this->_memory_size); $data = $this->return_skill_list(); shmop_write($shmid, json_encode($data), 0); @shmop_close($shmid); return $data; } $data = json_decode(preg_replace('/[\x00-\x1F\x80-\x9F]/u', '', trim(shmop_read($shmid, 0, $this->_memory_size))), true); @shmop_close($shmid); return $data; } public function return_skill_list() { //这里是一个超大的数组,其实就是把这个数组json化,而后存入共享内存段。 其实能够用redis等其余缓存...这里我就是为了避免用redis等其余nosql才用的shmop return array ( => array ('id' => '1','animation' => '13','skill_type' => '1','power_type' => '1','site' => '1','type' => '1','paramete' => '0','paramete2' => '0','paramete3' => '0','chance' => '0','ratio' => '1', ), => array ('id' => '2','animation' => '3','skill_type' => '2','power_type' => '1','site' => '1','type' => '1','paramete' => '0','paramete2' => '0','paramete3' => '0','chance' => '0','ratio' => '2', ),..........................................
固然你要考虑的是,若是数据更新的话,那么内存段也要删除,而且更新数据......................经过shmop_delete能够删除 。这就须要大家本身根据项目应用来考虑了
还有就是这篇文章只是为了简单的读,并无出现复杂的读写,不然可能会出现进程互斥等意想不到的冲突~若是复杂,那么就能够考虑信号量了~