http://www.cnblogs.com/fengwei/archive/2012/09/12/2682646.html

php作为脚本程序,通常生命周期都很短,如在web应用中,一次请求就是php运行的 一个周期,请求结束则生命周期截止。所以php在处理需要共 享的资源时,一般会将共享数据保存在数据库或dbm之类的文件中,再者就是利用内存实现共享。你可以选择已有的工具辅助你,像memcache;也可以自 己编写代码访问操作系统的共享内存段。

php中对共享内存段的操作有两组函数:System V IPCShared Memory。 其中System V IPC系列函数能够更方便的操作数据,无需像Shared Memory那样必须自己掌握读写时的偏移量、长度等,也不用序列化/反序列化来回转换(因为Shared Memory函数只支持字符串格式的数据参数)。但是System V IPC系列不支持Windows,所以如果要在win环境下使用,只能选Shared Memory。

因为php默认不支持这些函数,所以需要重编译php。如要使用:
System V信号量,编译时加上 –enable-sysvsem
System V共享内存,编译时加上 –enable-sysvshm
System V消息队列,编译时加上 –enable-sysvmsg
Shared Memory,编译时加上 –enable-shmop

先写个Shared Memory的例子:

<?php
$key = ftok(__FILE__, 'i');
$size = 100;
$shm_h = @shmop_open($key, 'c', 0644, $size);
if($shm_h === false) {
        echo "shmop open failed";
        exit;
}
$data = shmop_read($shm_h, 0, $size);
$data = unserialize($data);
//如果没有数据则写一个
if(empty($data)) {
        echo "there is no data";
        $data = "imdonkey";
        //就算数据是文本,write时也要序列化
        $write_size = shmop_write($shm_h, serialize($data), 0);
        if($write_size === false) echo "shmop write failed!";
}
//如果有,显示出来,之后删掉
else {
        echo "shared memory data: ";
        print_r($data);
        shmop_delete($shm_h);
}
shmop_close($shm_h);
?>

再写个System V shm的例子:
<?php 
$shm_key = ftok(__FILE__, 'i');
$memsize = 120;
$shm_h = shm_attach($shm_key, $memsize, 0644);
if($shm_h === false) {
echo "shmop open failed";
exit;
}
$var_key = 3;
$data = @shm_get_var($shm_h, $var_key);
if(empty($data)) {
$data = "imdonkey";
echo "there is no data, insert $data.\n";
shm_put_var($shm_h, $var_key, $data);
} else {
echo "find data: $data\n";
shm_remove_var($shm_h, $var_key);
}
shm_detach($shm_h);
?>

可以看到,sysV对于每个数据都另外设立了对应的var_key,这样在同一内存区域可以保存多个数据,而不用像shmop中那样再申请另外一个共享内存区域,还免除了序列化的干扰(虽然数据最终还是以序列化的形式保存,但不用开发者去手动实现)。

例子虽然简单,但也有一些需要注意的地方,不管是shm_attach还是shmop_open,所申请的内存的大小一定要满足后面数据的体积,这 个体积包括数据本身序列化后的长,还有php添加的少量header信息。php官方文档中有人提出了一种计算要申请的内存大小的公式,这个公式可以保证 所申请的内存足够存储一个指定的数据。公式如下:

//当shm_attach第一次被调用时,php向共享内存写入一个header
$shmHeaderSize = (PHP_INT_SIZE * 4) + 8;
//当shm_put_var调用时,php会在序列化后的数据前面,加一个header
$shmVarSize = (((strlen(serialize($foo))+ (4 * PHP_INT_SIZE)) /4 ) * 4 ) + 4;
$memsize = $shmHeaderSize + $shmVarSize;

这个公式是否适用于所有情况,我不敢说,所以我想最好还是在程序中,将准备放入共享内存的数据结构设计好,尽量保证数据大小在某一范围内。

还有就是为了防止共享内存被浪费,当数据无用时及时调用对应的remove方法释放资源。

介绍完共享内存再顺带提一下消息队列Message Queue(也是在System V IPC函数组中),消息队列似乎可以视为另一种共享内存,只是数据存储的方式有些不同。简单来说,就是每个key对应一个队列,每个队列可以保存多个数据,数据间按照先进先出的原则进行操作。php文档中的例子很好的介绍了各函数的应用:

<?php 
if ( sizeof($argv)<2 ) { 
        echo "Usage: $argv[0] stat|send|receive|remove msgType MSG [msg] \n\n" ; 
        echo "   EX: $argv[0] send 1 \"This is no 1\" \n" ; 
        echo "       $argv[0] receive ID \n" ; 
        echo "       $argv[0] stat \n" ; 
        echo "       $argv[0] remove \n" ; 
        exit; 

 
$MSGKey = "123456" ; 
$seg = msg_get_queue($MSGKey) ; 
 
switch ( $argv[1] ) { 
    case "send": 
        msg_send($seg, $argv[2], $argv[3]); 
        echo "msg_send done...\n" ; 
        break; 
 
    case "receive": 
        $stat = msg_stat_queue( $seg ); 
        echo 'Messages in the queue: '.$stat['msg_qnum']."\n"; 
        if ( $stat['msg_qnum']>0 ) { 
            msg_receive($seg, $argv[2], $msgtype, 1024, $data, true, MSG_IPC_NOWAIT); 
            var_dump($msgtype); 
            var_dump($data); 
            echo "\n"; 
        } 
        else { 
            echo "No Msg...\n"; 
        } 
        break; 
 
    case "stat": 
      print_r( msg_stat_queue($seg) ); 
        break; 
 
    case "remove": 
        msg_remove_queue($seg); 
        break; 

?>

消息队列中的数据同样受到大小的约束,具体约束范围可通过msg_stat_queue的msg_qbytes看到。这段代码唯一有点小改动的地方就在接受消息时,指定了MSG_IPC_NOWAIT,不然如果目标队列没有数据,默认会一直等待。

一般会用到共享内存或消息队列的情况,都会涉及到多线程/进程,或跨语言的数据传递。如果是php脚本/进程间共享数据,那只要小心点操作就没什么
问题。如果要求跨语言,那很可能遇到千奇百怪的问题,呵呵,我还没试过,但在网上看到别人发的苦水贴,以后有机会一定实验一下。

在调试共享内存、信号量、消息队列时,可以配合Linux系统命令观察数据存储情况及信号量、消息队列资源分配情况,如ipcs, ipcrm命令。

利用PHP操作Linux消息队列完成进程间通信

  当我们开发的系统需要使用多进程方式运行时,进程间通信便成了至关重要的环节。消息队列(message queue)是Linux系统进程间通信的一种方式。

  关于Linux系统进程通信的概念及实现可查看:http://www.ibm.com/developerworks/cn/linux/l-ipc/

  关于Linux系统消息队列的概念及实现可查看:http://www.ibm.com/developerworks/cn/linux/l-ipc/part4/
  PHP的sysvmsg模块是对Linux系统支持的System V IPC中的System V消息队列函数族的封装。我们需要利用sysvmsg模块提供的函数来进进程间通信。先来看一段示例代码_1:

<?php
 
$message_queue_key ftok(__FILE__'a');
 
$message_queue = msg_get_queue($message_queue_key, 0666);
var_dump($message_queue);
 
$message_queue_status = msg_stat_queue($message_queue);
print_r($message_queue_status);
 
//向消息队列中写
msg_send($message_queue, 1, "Hello,World!");
 
$message_queue_status = msg_stat_queue($message_queue);
print_r($message_queue_status);
 
//从消息队列中读
msg_receive($message_queue, 0, $message_type, 1024, $message, true, MSG_IPC_NOWAIT);
print_r($message."\r\n");
 
msg_remove_queue($message_queue);
 
?>  
这段代码的运行结果如下:

resource(4) of type (sysvmsg queue)
Array
(
    [msg_perm.uid] => 1000
    [msg_perm.gid] => 1000
    [msg_perm.mode] => 438
    [msg_stime] => 0
    [msg_rtime] => 0
    [msg_ctime] => 1279849495
    [msg_qnum] => 0
    [msg_qbytes] => 16384
    [msg_lspid] => 0
    [msg_lrpid] => 0
)
Array
(
    [msg_perm.uid] => 1000
    [msg_perm.gid] => 1000
    [msg_perm.mode] => 438
    [msg_stime] => 1279849495
    [msg_rtime] => 0
    [msg_ctime] => 1279849495
    [msg_qnum] => 1
    [msg_qbytes] => 16384
    [msg_lspid] => 2184
    [msg_lrpid] => 0
)
Hello,World!

可以看到已成功从消息队列中读取“Hello,World!”字符串

  下面列举一下示例代码中的主要函数:

ftok ( string $pathname , string $proj )
    手册上给出的解释是:Convert a pathname and a project identifier to a System V IPC key。这个函数返回的键值唯一对应linux系统中一个消息队列。在获得消息队列的引用之前都需要调用这个函数。
 
msg_get_queue ( int $key [, int $perms ] )
    msg_get_queue()
会根据传入的键值返回一个消息队列的引用。如果linux系统中没有消息队列与键值对应,msg_get_queue()将会创建一个新的消息队列。函数
的第二个参数需要传入一个int值,作为新创建的消息队列的权限值,默认为0666。这个权限值与linux命令
chmod中使用的数值是同一个意思,因为在linux系统中一切皆是文件。
 
msg_send ( resource $queue , int $msgtype , mixed $message [, bool $serialize [, bool $blocking [, int &$errorcode ]]] )
    顾名思义,该函数用来向消息队列中写数据。
 
msg_stat_queue ( resource $queue )
    这个函数会返回消息队列的元数据。消息队列元数据中的信息很完整,包括了消息队列中待读取的消息数、最后读写队列的进程ID等。示例代码在第8行调用该函数返回的数组中队列中待读取的消息数msg_qnum值为0。
 
msg_receive ( resource $queue , int $desiredmsgtype , int &$msgtype , int $maxsize , mixed &$message [, bool$unserialize [, int $flags [, int &$errorcode ]]] )
    msg_receive用于读取消息队列中的数据。
 
msg_remove_queue ( resource $queue )
    msg_remove_queue用于销毁一个队列。
示例代码_1只是展示了PHP操作消息队列函数的应用。下面的代码具体描述了进程间通信的场景

<?php
 
$message_queue_key ftok(__FILE__'a');
$message_queue = msg_get_queue($message_queue_key, 0666);
 
$pids array();
for ($i = 0;  $i < 5; $i++) {
        //创建子进程
        $pids[$i] = pcntl_fork();
 
        if ($pids[$i]) {
                echo "No.$i child process was created, the pid is $pids[$i]\r\n";
        elseif ($pids[$i] == 0) {
                $pid = posix_getpid();
                echo "process.$pid is writing now\r\n";
 
                msg_send($message_queue, 1, "this is process.$pid's data\r\n");
                posix_kill($pid, SIGTERM);
        }
}
 
do {
        msg_receive($message_queue, 0, $message_type, 1024, $message, true, MSG_IPC_NOWAIT);
        echo $message;
 
        //需要判断队列是否为空,如果为空就退出
        //break;
while(true)
 
?>
运行结果为:

No.0 child process was created, the pid is 5249
No.1 child process was created, the pid is 5250
No.2 child process was created, the pid is 5251
No.3 child process was created, the pid is 5252
No.4 child process was created, the pid is 5253
process.5251 is writing now
this is process.5251's data
process.5253 is writing now
process.5252 is writing now
process.5250 is writing now
this is process.5253's data
this is process.5252's data
this is process.5250's data
process.5249 is writing now
this is process.5249's data

这段程序每次的运行结果都会不同,这正说明了多进程的异步性。从结果也能看出消息队列FIFO特性。

以上便是我研究的一点心得。接下来将会继续研究PHP利用信号、socket等进行进程间通信的方法。

php中对共享内存,消息队列的操作的更多相关文章

  1. Akka系列(四):Akka中的共享内存模型

    前言...... 通过前几篇的学习,相信大家对Akka应该有所了解了,都说解决并发哪家强,JVM上面找Akka,那么Akka到底在解决并发问题上帮我们做了什么呢? 共享内存 众所周知,在处理并发问题上 ...

  2. Windows中利用共享内存来实现不同进程间的通信

    Windows中利用共享内存来实现不同进程间的通信 一.msdn详细介绍 https://docs.microsoft.com/zh-cn/windows/win32/memory/sharing-f ...

  3. openstack介绍及共享组件——消息队列rabbitmq

    一.云计算的前世今生 所有的新事物都不是突然冒出来的,都有前世和今生.云计算也是IT技术不断发展的产物. 要理解云计算,需要对IT系统架构的发展过程有所认识. 请看下 IT系统架构的发展到目前为止大致 ...

  4. 云计算共享组件--消息队列rabbitmq(3)

    一.MQ 全称为 Message Queue, 消息队列( MQ ) 是一种应用程序对应用程序的通信方法.应用程序通过读写出入队列的消息(针对应用程序的数据)来通信,而无需专用连接来链接它们. 消息传 ...

  5. 云计算openstack共享组件——消息队列rabbitmq(3)

    一.MQ 全称为 Message Queue, 消息队列( MQ ) 是一种应用程序对应用程序的通信方法.应用程序通过读写出入队列的消息(针对应用程序的数据)来通信,而无需专用连接来链接它们. 消息传 ...

  6. ucos中信号量 事件标志 消息队列都怎么用

    信号量 事件标志和消息队列分别应用于什么场景(反正我学的时候有点闹不清,现在总结一下): 信号量和事件标志用于任务同步.详细来说,这个功能可以替代以前裸机中你打一个标记的功能,比如使用了一个定时器,5 ...

  7. SpringBoot学习笔记(11)-----SpringBoot中使用rabbitmq,activemq消息队列和rest服务的调用

    1. activemq 首先引入依赖 pom.xml文件 <dependency> <groupId>org.springframework.boot</groupId& ...

  8. SpringBoot中使用rabbitmq,activemq消息队列和rest服务的调用

    1. activemq 首先引入依赖 pom.xml文件 <dependency> <groupId>org.springframework.boot</groupId& ...

  9. ipcclean - 从退出的PostgreSQL服务器中删除共享内存和信号灯

    SYNOPSIS ipcclean DESCRIPTION 描述 ipcclean 删除当前用户拥有的所有共享内存段和信号灯集. 它的目地是在 PostgreSQL 服务器 (postmaster(1 ...

随机推荐

  1. 20141014--判断语句switch case

    1. switch case break(跳出) 距离break 最近的大括号,跳出这个大括号,执行大括号之后的命令 switch case 必须与 break 一同使用. 分别输入月份 几号 输出是 ...

  2. OC12_自动释放池

    // // Dog.h // OC12_自动释放池 // // Created by zhangxueming on 15/6/18. // Copyright (c) 2015年 zhangxuem ...

  3. MAC系统介绍

    MACOS: UNIX系统图形界面的显示 开发环境: 一种是终端(terminal) 一种是Xcode(ide) MAC快捷键: command(window) + c : 复制 command + ...

  4. rolling hash

    也是需要查看,然后修改,rolling hash, recursive hash, polynomial hash, double hash.如果一次不够,那就2次.需要在准备一个线段树,基本的线段树 ...

  5. Java流的正确关闭方式

    因为流是无论如何一定要关闭的,所以要写在finally里.如下: BufferedReader reader = null; try { reader = (BufferedReader) getRe ...

  6. Hibernate Cascade & Inverse

    Cascade - 修改实体表 Inverse - 修改中间表 http://www.cnblogs.com/amboyna/archive/2008/02/18/1072260.html 1.到底在 ...

  7. [牛感悟系列]JAVA(1)理解JAVA垃圾回收

    理解JAVA垃圾回收的好处是什么?满足求知欲是一方面,编写更好的JAVA应用是另外一方面. 如果一个人对垃圾回收过程感兴趣,那表明他在应用程序开发领域有相当程度的经验.如果一个人在思考如何选择正确的垃 ...

  8. mvc 之 @Html.DropDownList

    Dictionary<string, string> myDic = new Dictionary<string, string>(); myDic.Add(System.DB ...

  9. C# 学习之旅(3) --- 会说话的简易计算器

    using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; usin ...

  10. 字符设备驱动中cdev与inode、file_operations的关系

    一.cdev与inode 二.cdev与file_operations