[备案待实践]在PHP中实现进程间通讯之共享内存

jackxiang 2009-7-22 18:02 | |
谁有php操作共享内存的代码?


APC这个方案不错,得看:http://leepiao.blog.163.com/blog/static/48503130201121811722907/
$shm_id   =   shmop_open(0xff3,   "c",   0644,   100);  
用这句后出现以下错误,怎样解决  
  Call   to   undefined   function:   shmop_open()
解决方法:
Linux:(共享内存只适用于linux兼容系统)
php编译的时候,configure   后面加   --enable-shmop
Windows:
win,php.ini中   extension=php_shmop.dll   前的分号去掉,重启web服务器 !
必须参考:http://blog.csdn.net/laruence/archive/2008/04/15/2294451.aspx
<?php

if($stop!=1)
{
// 判断是否已经创建
$shm_id = @shmop_open(0xff3, "a", 0644, 100);
if($shm_id) {
echo "内存块已经创建\n";
exit();
}
// 创建 100 字节的系统标识为 0xff3 的共享内存块
$shm_id = @shmop_open(0xff3, "c", 0644, 100);
if(!$shm_id) {
echo "共享内存块创建失败\n";
}

// 写一些内容
for($i=0;$i<10;$i++)
{
$shm_bytes_written = shmop_write($shm_id, "共享块$i", ($i*10));
}
echo "创建成功";
shmop_close($shm_id);
}
else
{
// 是否存在
$shm_id = @shmop_open(0xff3, "a", 0644, 100);
if(!$shm_id) {
echo "没有找到共享内存块\n";
exit();
}
// 删除
if(!shmop_delete($shm_id)) {
echo "删除失败.";
}
shmop_close($shm_id);
echo "内存块删除完毕!";
}
?>


2 share2.php 用于显示共享内存的内容


<?php

// 打开共享内存块 0xff3
$shm_id = @shmop_open(0xff3, "a", 0644, 100);
if(!$shm_id) {
echo "不能打开共享内存块\n";
exit();
}

// 读出里面的内容
$my_string = shmop_read($shm_id, 0, $shm_size);
if(!$my_string) {
echo "没有内容或者不能读\n";
}
echo $my_string."\n";
shmop_close($shm_id);
?>


最近想用它做点什么,可是思想还不成熟,暂时先放个例子在这里,以便以后使用。

共享内存首先是使用内存空间,虽然不是直接访问内存地址,由于不受磁盘io和网络带宽限制,在性能方面,共享内存比其它数据交互方式有巨大的优势。跟memcache比,这个共享内存不需要依赖其他的服务,只需要php编译时加入shmop的支持即可。
其次,它是共享的,同一台机器上所有的程序,都有访问它的权利。常做进程间通信用。php程序,每次执行完,生命也就结束了。
但是共享内存中的数据不一样,直到销毁前,它能让一份数据一直保留在内存中。使后来的程序能够使用它。

下面是一个使用共享内存做的简单计数器。能很高效的记录次数,缺点是内存数据不能永久保存,可以用daemon每隔几分钟同步一次共享内存与数据库。


?php
$shm_id = shmop_open(0x00000001, "c", 0644, 4);     //通过一个指定的key来打开一个共享内存,
                                                    //这个key可以是自己指定,也可以用ftok生成
$data = unpack('Ncount',shmop_read($shm_id, 0, 4)); //解包数据
$count = $data['count']+1;                          //加一
shmop_write($shm_id, pack('N',$count), 0);          //打包数据
shmop_close($shm_id);                               //关闭共享内存
echo $count;                                        //输出数字
exit(0);                                            //退出
?>
命令行查看
        ipcs -h for help.

[root@localhost html]# ipcs -m

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status    
0x00000001 262144     root      644        4          0                      

[root@localhost html]#


可以看到,共享内存的key, shmid,所有者,权限,大小,attach数,和状态。

删除共享内存可以用ipcrm -m shmid或ipcrm -M shmkey命令来完成。


[root@localhost html]# ipcrm -M 0x00000001
[root@localhost html]# ipcs -m

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status    

[root@localhost html]#


看,现在那份放在共享内存中的数据已经被删掉了。


本文将讨论在PHP4环境下如何使用进程间通讯机制——IPC(Inter-Process-Communication)。本文讨论的软件环境是linux+php4.0.4 或更高版本。首先,我们假设你已经装好了PHP4和UNIX, 为了使得php4可以使用共享内存和信号量,必须在编译php4程序时激活shmop和sysvsem这两个扩展模块。
实现方法:在PHP设定(configure)时加入如下选项。
--enable-shmop --enable-sysvsem
这样就使得你的PHP系统可以处理相关的IPC函数了。
IPC是什么?
IPC (Inter-process communication) 是一个Unix标准通讯机制,它提供了使得在同一台主机不同进程之间可以互相通讯的方法。基本的IPC处理机制有3种:它们分别是共享内存、信号量和消息队列。本文中我们主要讨论共享内存和信号量的使用。关于消息队列,笔者在不久的将来还会专门介绍。
在PHP中使用共享内存段
在不同的处理进程之间使用共享内存是一个实现不同进程之间相互通讯的好方法。如果你在一个进程中向所共享的内存写入一段信息,那么所有其他的进程也可以看到这段被写入的数据。非常方便。在PHP中有了共享内存的帮助,你可以实现不同进程在运行同一段PHP脚本时返回不同的结果。或实现对PHP同时运行数量的实时查询等等。
共享内存允许两个或者多个进程共享一给定的存储区。因为数据不需要在客户机和服务器之间复制,所以这是最快的一种IPC。使用共享内存的唯一窍门是多个进程对一给定存储区的同步存取。
如何建立一个共享内存段呢?下面的代码可以帮你建立共享内存。
$shm_id = shmop_open($key, $mode, $perm, $size);
注意,每个共享内存段都有一个唯一的ID, 在PHP中,shmop_open会把建立好的共享内存段的ID返回,这里我们用$shm_id记录它。而$key是一个我们逻辑上表示共享内存段的 Key值。不同进程只要选择同一个Key id就可以共享同一段存储段。习惯上我们用一个串(类似文件名一样的东西)的散列值作为key id. $mode指明了共享内存段的使用方式。这里由于是新建,因此值为’c’ –取create之意。如果你是访问已经建立过的共享内存那么请用’a’,-- 取access之意。$perm参数定义了访问的权限,8进制,关于权限定义请看UNIX文件系统帮助。$size定义了共享内存的大小。尽管有点象 fopen(文件处理)你可不要当它同文件处理一样。后面的描述你将看到着一点。
例如:
$shm_id = shmop_open(0xff3, "c", 0644, 100);
这里我们打开了一个共享内存段 键值0xff3 –rw-r—r—格式,大小为100字节。
如果需要访问已有的共享内存段,你必须在调用shmop_open中设第3、4个参数为0。
IPC工作状态的查询
在Unix下,你可以用一个命令行程序ipcs查询系统所有的IPC资源状态。不过有些系统要求需要超级用户方能执行。下图是一段ipcs的运行结果。
点击在新窗口中浏览此图片

上图中系统显示了4个共享内存段,注意其中第4个键值为0x00000ff3的就是我们刚刚运行过的PHP程序所创建的。关于ipcs的用法请参考UNIX用户手册。
如何释放共享内存呢
释放共享内存的办法是调用PHP指令:shmop_delete($id)
shmop_delete($id);
$id 就是你调用shmop_open所存的shmop_op的返回值。还有一个办法就是用UNIX的管理指令:
ipcrm id, id就是你用ipcs看到的ID.和你程序中的$id不一样。不过要小心,如果你用ipcrm直接删除共享内存段那么有可能导致其他不知道这一情况的进程在引用这个已经不复存在的共享内存器时出现一些不可预测的错误(往往结果不妙)。
如何使用(读写)共享内存呢
使用如下所示函数向共享内存写入数据
int shmop_write (int shmid, string data, int offset)
其中shmid是用shmop_open返回的句柄。$Data变量存放了要存放的数据。$offset描述了写入从共享内存的开始第一个字节的位置(以0开始)。
读取操作是:
string shmop_read (int shmid, int start, int count)
同样,指明$shmid,开始偏移量(以0开始)、总读取数量。返回结果串。这样,你就可以把共享内存段当作是一个字节数组。读几个再写几个,想干嘛就干嘛,十分方便。
多进程问题的考虑
现在,在单独的一个PHP进程中读写、创建、删除共享内存方面上你应该没有问题了。但是,显然实际运行中不可能只是一个PHP进程在运行中。如果在多个进程的情况下你还是沿用单个进程的处理方法,你一定会碰到问题 ---- 著名的并行和互斥问题。比如说有2个进程同时需要对同一段内存进行读写。当两个进程同时执行写入操作时,你将得到一个错误的数据,因为该段内存将之可能是最后执行的进程的内容,甚至是由2个进程写入的数据轮流随机出现的一段混合的四不象。这显然是不能接受的。为了解决这个问题,我们必须引入互斥机制。互斥机制在很多操作系统的教材上都有专门讲述,这里不多重复。实现互斥机制的最简单办法就是使用信号灯。信号量是另外一种进程间通讯(IPC)的方式,它同其他IPC机构(管道、FIFO、消息队列)不同。它是一个记数器,用于控制多进程对共享数据的存储。同样的是你可以用ipcs和ipcrm实现对信号灯使用状态的查询和对其实现删除操作。在PHP中你可以用下列函数创建一个新的信号量并返回操作该信号量的句柄。如果该key指向的信号量已经存在,sem_get直接返回操作该信号量的句柄。
int sem_get (int key [, int max_acquire [, int perm]])
$max_acquire 指明同时最多可以用几个进程进入该信号而不必等待该信号被释放(也就是最大同时处理某一资源的进程数目,一般该值均为一)。$perm指明了访问权限。
一旦你成功的拥有了一个信号量,你对它所能做的只有2种:请求、释放。当你执行释放操作时, 系统将把该信号值减一。如果小于0那就还设为0。而当你执行请求操作时,系统将把该信号值加一,如果该值大于设定的最大值那么系统将挂起你的处理进程直到其他进程释放到小于最大值为止。一般情况下最大值设为1,这样一来当一个进程获得请求时其他后面的进程只能等待它退出互斥区后释放信号量才能进入该互斥区并同时设为独占方式。这样的信号量常称为双态信号量。当然,如果初值是任意一个正数就表明有多少个共享资源单位可供共享应用。
申请、释放操作的PHP格式如下:
int sem_acquire (int sem_identifier) 申请
int sem_release (int sem_identifier) 释放
其中sem_identifier是调用sem_get的返回值(句柄)。
一个简单的互斥协议例子
下面是一段很简单的互斥操作规程。
$semid=sem_get(0xee3,1,0666);
$shm_id = shmop_open(0xff3, "c", 0644, 100);
sem_acquire($semid);      //申请
/* 进入临界区*/
  这里,对共享内存进行处理
sem_release($semid);      //释放
正如你所看到的,互斥的实现很简单:申请进入临界区,对临界区资源进行操作(比如修改共享内存)退出临界区并释放信号。这样一来就可以保证在同一个时间片中不可能有同时2个进程对同一段共享内存进行操作。因为信号量机制保证一个时间片只能由一个进程进入,其他进程必须等待当前处理的进程完成后方能进入。
临界区一般是指那些不允许同时有多个进程并发处理的代码段。
要注意的是:在PHP中必须由同一个进程释放它所占用的信号量。在一般系统中允许进程释放别的进程占用的信号。在编写临界区代码一定要小心设计资源的分配,避免A等B,B等A的死锁情况发生。
运 用
IPC的运用是十分广泛的。比如,在不同进程间保存一个解释过的复杂的配置文件、或具体设置的用户等,以避免重复处理。我也曾经用共享内存的技术把一大批 PHP脚本必须引用的一个很大的文件放入共享内存,并由此显著提升了Web服务的速度、消除了部分瓶颈。关于它的使用还有聊天室,多路广播等等。IPC的威力取决于你的想象力的大小。如果本文对你有一点点启发,那我不胜荣幸。愿意很你讨论这令人入迷的电脑技术。Email: qwyaxm@163.net

共享内存首先是使用内存空间,虽然不是直接访问内存地址,由于不受磁盘io和网络带宽限制,在性能方面,共享内存比其它数据交互方式有巨大的优势。跟memcache比,这个共享内存不需要依赖其他的服务,只需要php编译时加入shmop的支持即可。
其次,它是共享的,同一台机器上所有的程序,都有访问它的权利。常做进程间通信用。php程序,每次执行完,生命也就结束了。
但是共享内存中的数据不一样,直到销毁前,它能让一份数据一直保留在内存中。使后来的程序能够使用它。

下面是一个使用共享内存做的简单计数器。能很高效的记录次数,缺点是内存数据不能永久保存,可以用daemon每隔几分钟同步一次共享内存与数据库。

?php
$shm_id = shmop_open(0x00000001, "c", 0644, 4);     //通过一个指定的key来打开一个共享内存,
                                                    //这个key可以是自己指定,也可以用ftok生成
$data = unpack('Ncount',shmop_read($shm_id, 0, 4)); //解包数据
$count = $data['count']+1;                          //加一
shmop_write($shm_id, pack('N',$count), 0);          //打包数据
shmop_close($shm_id);                               //关闭共享内存
echo $count;                                        //输出数字
exit(0);                                            //退出
?>
命令行查看
        ipcs -h for help.
[root@localhost html]# ipcs -m

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status      
0x00000001 262144     root      644        4          0                      

[root@localhost html]#

可以看到,共享内存的key, shmid,所有者,权限,大小,attach数,和状态。

删除共享内存可以用ipcrm -m shmid或ipcrm -M shmkey命令来完成。

[root@localhost html]# ipcrm -M 0x00000001
[root@localhost html]# ipcs -m

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status      

[root@localhost html]#

看,现在那份放在共享内存中的数据已经被删掉了。

用共享内存来记数,一段时间后写入硬盘中的示例:



参考:http://www.5dphp.com/tag-%25C4%25DA%25B4%25E6.html
http://www.cnblogs.com/yidongyou/articles/1568839.html
自己网站参考:
http://jackxiang.com/post/1852/
http://jackxiang.com/post/3033/
http://jackxiang.com/post/3441/
http://jackxiang.com/post/3977/



这段时间在折腾些PHP中共享内存的方案。
提到PHP共享内存朋友们都说:怎么不用memcache呢?
之所以不用memcache主要有两方面原因:
1、从官方下载的都是源文件需要编译,觉得太麻烦了。
2、在单台服务器的情况下,memcache这种客户端/服务端模式共享内存方案速度较APC这种服务器直接操作内存慢。

Alternative PHP Cache(APC)是 PHP 的一个免费公开的优化代码缓存。它用来提供免费,公开并且强健的架构来缓存和优化 PHP 的中间代码。
APC是PECL(PHP Extension Community Library)扩展的一部份,未捆绑在PHP安装文件中。关于APC的安装,网上有很多讲安装配置的文章。
不过我安装时下载地址找了很久,该扩展的官方地址是http://pecl.php.net/package/APC,但这上面提供的都是未编译的文件。粗略看了下,在WIN32上编译PHP源码(查看)。

PECL fro Windows站点(http://pecl4win.php.net/)目前也暂时关闭了,后来发现原来PHP官方的这个页面提供下载编译了的APC。

安装很简单,修改php.ini文件,添加extension=php_apc.dll即可。

折腾了一番,下面是用APC共享内存时经常会用到的函数:

bool apc_add ( string $key , mixed $var [, int $ttl = 0 ] )
添加一组键值到内存中,如果$key已经存在则返回false。变量生存周期$ttl单位为秒。

bool apc_store ( string $key , mixed $var [, int $ttl = 0 ] )
添加一组键值到内存中,如果$key已经存在则覆盖。变量生存周期$ttl单位为秒。

mixed apc_exists ( mixed $keys )
检查内存中是否存在键$keys。

mixed apc_fetch ( mixed $key [, bool &$success ] )
从内存中取出键$key对应的值。

bool apc_delete ( string $key )
从内存中删除键名为$key的值。
更多函数请参考这里:http://cn.php.net/manual/en/book.apc.php

来自:http://i.80tvb.com/php-apc-memory/

作者:jackxiang@向东博客 专注WEB应用 构架之美 --- 构架之美,在于尽态极妍 | 应用之美,在于药到病除
地址:https://jackxiang.com/post/1852/
版权所有。转载时必须以链接形式注明作者和原始出处及本声明!


最后编辑: jackxiang 编辑于2014-5-17 14:05
评论列表
发表评论

昵称

网址

电邮

打开HTML 打开UBB 打开表情 隐藏 记住我 [登入] [注册]