Linux下的共享内存有三种方式:sysv,posix,mmap。但是php只提供了操作sysv的函数,所以下面要说的就是sysv的共享内存,Linux系统提供了三个工具来满足IPC通讯:消息队列,共享内存段,信号量。
ipcs 命令
root@localhost:/usr/local/bin# ipcs
------ Message Queues --------
key msqid owner perms used-bytes messages
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
------ Semaphore Arrays --------
key semid owner perms nsems
PHP提供的扩展,不需要安装,但是需要手动开启。
shmop 共享内存,只能按字节操作
sysvsem 信号量
sysvshm 共享内存,和 shmop 的差别是提供的操作函数不同,支持 key、value操作
sysvmsg 消息队列
sysvsem,sysvshm,sysvmsg 的函数列表
shmop 的函数列表
看来 sysvshm 扩展和 shmop 扩展功能差不多。
sysvshm 扩展提供的方法在存储之前对用户的数据进行serialize处理,这里就导致这个存储的数据是无法与其它语言共享的,这一系列方法是php only的方法。
shmop系列函数,虽然能实现共享内存操作,但实际上底层实现非常简陋。一方面底层根本没有加锁,如果你要在并发环境中使用,需要自行实现锁的操作。另外,底层实际上是一个链表结构,数据较多时,查询性能非常差。
无论是Linux还是Windows,都会提供一定大小的内存给应用程序来使用,共享内存就像一个设置了权限的文件,允许你像操作文件一样操作共享内存。
比如,PHP多进程之间的通讯,你可以使用临时文件,管道,或者共享内存来在进程间交换数据;并且,你也可以通过共享内存来实现不同应用程序间的通讯。
1、查看已设置的共享内存中的内存段列表
ipcs -m
------------ 共享内存段 --------------
键key shmid 拥有者owner 权限perms 字节bytes nattch 状态status
0x00000360 0 root 755 1024 0
以上是有可用的片段的情况,是不是和文件的形式很相似。
字段解释:
key :共享内存的唯一的key值,共享内存通过该key来判断你读取的是哪一块内存。
shmid :当使用key来获取内存时,你获得的是这个id的值。它作为你操作内存块的标识,类型为 resource(4) of type (shmop)
owner :创建该共享内存块的用户
perms :该共享内存的读写权限,8禁止,可以是777,与文件的读写权限一致。
bytes :该内存块的大小,字节
nattch :连接该内存块的进程数
status :当前状态,如:dest,即将删除等。
key是一个16进制值,我们也可以传10进制值
一般操作:通过key来打开一个内存段,返回shmid,之后通过这个shmid来操作内存段。
查看系统共享内存大小
cd /proc/sys/kernel
cat shmmax
删除共享内存块
ipcrm -m shmid
或者
ipcrm -M key
shmop 系列函数
PHP通过shmop函数来操作共享内存,非常之方便。
shmop_open()
它允许您打开一个现有的内存段或创建一个新内存段。此函数非常类似于经典的fopen函数.
$shmid = shmop_open(864, 'c', 0755, 1024);
第一个参数就是上面的key,如果不存在就会创建,十进制的 864 就是16进制的 0x00000360,都可以使用。返回值就是shmid,失败返回FALSE
第二个参数是访问模式
模式 “a”,它允许您访问只读内存段
模式 “w”,它允许您访问可读写的内存段
模式 “c”,它创建一个新内存段,或者如果该内存段已存在,尝试打开它进行读写
模式 “n”,它创建一个新内存段,如果该内存段已存在,则会失败
第三个参数为权限
第四个参数为大小,字节
shmop_write()
写入数据,失败时会返回 FALSE,在成功时会返回写入的字节数。
shmop_write($shmid, "Hello World!", 0);
第三个参数为offset
shmop_read()
读取共享内存数据
$str = shmop_read($shmid, 0, 11);// offset, length
shmop_size()
获取指定片段的内存大小,字节
$size = shmop_size($shmid);
shmop_delete()
标记为删除,阻止任何其他进程打开它,并没有实际删除。因为本进程正在打开该片段,需要关闭后才会实际被删除。
shmop_close()
关闭内存段。
实例:
文件1
$shmid = shmop_open(864, 'c', 0755, 1024);// 0x00000360
shmop_write($shmid, "Hello World PHP!", 0); // 注意 data是字符串类型,不管你赋值的是不是字符串。
shmop_close($shmid);
查看
ipcs -m
文件2
$shmid = shmop_open(864, 'c', 0755, 1024);// 0x00000360
var_dump($shmid);// resource(4) of type (shmop)
$size = shmop_size($shmid);
echo shmop_read($shmid, 0, $size) . PHP_EOL;
shmop_close($shmid);
当然,也可以使用其他程序打开,比如java, c++等
并发的问题
共享内存的操作并不是原子性的,多个进程同时操作就存在并发的问题,需要通过信号量来加锁。
在共享数据方面,效率最高的是内存,其次是本地问文件,最慢的是网络IO(memcache, redis,mysql等)。