设为首页 收藏本站
查看: 1000|回复: 0

[经验分享] php进程间通信

[复制链接]

尚未签到

发表于 2017-12-30 21:33:55 | 显示全部楼层 |阅读模式
  首先我们来讲解一下,php如何实现共享内存。(注意:本示例是在linux下,请勿在windows下尝试此代码,并且必须是在php-cli模式下)
  php提供了两种实现共享内存的扩展。下面我们来一一讲解。
  一、shmop 系类函数
  

1 <?php  

2 /**  

3  * author: NickBai  

4  * createTime: 2016/12/5 0005 上午 9:17  

5  */  
6 $shm_key = ftok(__FILE__, 't');
  
7
  
8 /**
  
9 开辟一块共享内存
  
10
  
11 int $key , string $flags , int $mode , int $size
  
12 $flags: a:访问只读内存段
  
13 c:创建一个新内存段,或者如果该内存段已存在,尝试打开它进行读写
  
14 w:可读写的内存段
  
15 n:创建一个新内存段,如果该内存段已存在,则会失败
  
16 $mode: 八进制格式  0655
  
17 $size: 开辟的数据大小 字节
  
18
  
19  */
  
20
  
21 $shm_id = shmop_open($shm_key, "c", 0655, 1024);
  
22
  
23 /**
  
24  * 写入数据 数据必须是字符串格式 , 最后一个指偏移量
  
25  * 注意:偏移量必须在指定的范围之内,否则写入不了
  
26  *
  
27  */
  
28 $size = shmop_write($shm_id, 'hello world', 0);
  
29 echo "write into {$size}";
  
30
  
31 #读取的范围也必须在申请的内存范围之内,否则失败
  
32 $data = shmop_read($shm_id, 0, 100);
  
33 var_dump($data);
  
34
  
35 #删除 只是做一个删除标志位,同时不在允许新的进程进程读取,当在没有任何进程读取时系统会自动删除
  
36 shmop_delete($shm_id);
  
37
  
38 #关闭该内存段
  
39 shmop_close($shm_id);
  

  注意两点:
  1、shmop_read 函数 第2个参数 是读取的起始位置,第3个参数是要读取的长度,如果你要读取的长度小于信息长度,原信息会被截断成你指定的长度。
  2、shmop_write 函数 仅可写 字符串 内容!
  二、用 Semaphore 扩展中的 sem 类函数 (用起来更方便,类似 key-value 格式)
  

<?php  

/**  
* author: NickBai
  
* createTime: 2016/12/5 0005 上午 9:28
  

*/  
// Get the file token key
  
$key = ftok(__FILE__, 'a');
  
$shar_key = 1;
  

  
// 创建一个共享内存
  
$shm_id = shm_attach($key, 1024, 0666); // resource type
  
if ($shm_id === false) {
  
die('Unable to create the shared memory segment' . PHP_EOL);
  
}
  

  
#设置一个值
  
shm_put_var($shm_id, $shar_key, 'test');
  

  
#删除一个key
  
//shm_remove_var($shm_id, $shar_key);
  

  
#获取一个值
  
$value = shm_get_var($shm_id,  $shar_key);
  
var_dump($value);
  

  
#检测一个key是否存在
  
// var_dump(shm_has_var($shm_id,  $shar_key));
  

  
#从系统中移除
  
shm_remove($shm_id);
  

  
#关闭和共享内存的连接
  
shm_detach($shm_id);
  

  shm_put_var 第三个参数 写入的值 是一个混合类型,所以没有shmop_write的局限性。
  注意:$shar_key 只能是 int 型的参数。
  介绍完了php如何创建、操作共享内存,下面我们来看一下,他们如何在进程间通信发挥作用吧。
  

1 <?php  

2 /**  

3  * author: NickBai  

4  * createTime: 2016/12/5 0005 上午 10:26  

5  */  
6
  
7 //共享内存通信
  
8
  
9 //1、创建共享内存区域
  
10 $shm_key = ftok(__FILE__, 't');
  
11 $shm_id = shm_attach( $shm_key, 1024, 0655 );
  
12 const SHARE_KEY = 1;
  
13 $childList = [];
  
14
  
15 //2、开3个进程 读写 该内存区域
  
16 for( $i = 0; $i < 3; $i++ ){
  
17
  
18     $pid = pcntl_fork();
  
19     if( $pid == -1 ){
  
20         exit('fork fail!' . PHP_EOL);
  
21     }else if( $pid == 0 ){
  
22
  
23         //子进程从共享内存块中读取 写入值 +1 写回
  
24         if ( shm_has_var($shm_id, SHARE_KEY) ){
  
25             // 有值,加一
  
26             $count = shm_get_var($shm_id, SHARE_KEY);
  
27             $count ++;
  
28             //模拟业务处理逻辑延迟
  
29             $sec = rand( 1, 3 );
  
30             sleep($sec);
  
31
  
32             shm_put_var($shm_id, SHARE_KEY, $count);
  
33         }else{
  
34             // 无值,初始化
  
35             $count = 0;
  
36             //模拟业务处理逻辑延迟
  
37             $sec = rand( 1, 3 );
  
38             sleep($sec);
  
39
  
40             shm_put_var($shm_id, SHARE_KEY, $count);
  
41         }
  
42
  
43         echo "child process " . getmypid() . " is writing ! now count is $count\n";
  
44
  
45         exit( "child process " . getmypid() . " end!\n" );
  
46     }else{
  
47         $childList[$pid] = 1;
  
48     }
  
49 }
  
50
  
51 // 等待所有子进程结束
  
52 while( !empty( $childList ) ){
  
53     $childPid = pcntl_wait( $status );
  
54     if ( $childPid > 0 ){
  
55         unset( $childList[$childPid] );
  
56     }
  
57 }
  
58
  
59 //父进程读取共享内存中的值
  
60 $count = shm_get_var($shm_id, SHARE_KEY);
  
61 echo "final count is " . $count . PHP_EOL;
  
62
  
63
  
64 //3、去除内存共享区域
  
65 #从系统中移除
  
66 shm_remove($shm_id);
  
67 #关闭和共享内存的连接
  
68 shm_detach($shm_id);
  

  逻辑很简单,开启3个进程,对同一个共享内存中的数据进行读写。有一个count的值,如果读到就+1,下面我们看一下运行结果:
DSC0000.png

  从结果中我们可以看到,最终的 count 的值还是0。这是为什么呢?简单分析一下,不难发现,当我们开启创建进程的时候,3个子进程同时打开了 共享内存区域,此时他们几乎是同步的,所以读到的信息都是没有count值,此时他们执行自己的业务
  逻辑然后将 count 为0的结果写入内存区域。这并不是我们想要的结果,三个子进程互相抢占了资源,这是不合理的,那怎么规避这个问题呢?答案是通过 信号量 !
  信号量
  信号量是什么? 信号量 : 又称为信号灯、旗语 用来解决进程(线程同步的问题),类似于一把锁,访问前获取锁(获取不到则等待),访问后释放锁。
  举一个生活中的例子:以一个停车场的运作为例。简单起见,假设停车场只有三个车位,一开始三个车位都是空的。这时如果同时来了五辆车,看门人允许其中三辆直接进入,然后放下车拦,剩下的车则必须在入口等待,此后来的车也都不得不在入口
  处等待。这时,有一辆车离开停车场,看门人得知后,打开车拦,放入外面的一辆进去,如果又离开两辆,则又可以放入两辆,如此往复。在这个停车场系统中,车位是公共资源,每辆车好比一个线程,看门人起的就是信号量的作用
  下面我们来看一下信号量的几个函数:
  

1 <?php  

2 $key=ftok(__FILE__,'t');  

3  
4 /**
  
5  * 获取一个信号量资源
  
6  int $key [, int $max_acquire = 1 [, int $perm = 0666 [, int $auto_release = 1 ]]]
  
7  $max_acquire:最多可以多少个进程同时获取信号
  
8  $perm:权限 默认 0666
  
9  $auto_release:是否自动释放信号量
  
10  */
  
11 $sem_id=sem_get($key);
  
12
  
13 #获取信号
  
14 sem_acquire($seg_id);
  
15
  
16 //do something 这里是一个原子性操作
  
17
  
18 //释放信号量
  
19 sem_release($seg_id);
  
20
  
21 //把次信号从系统中移除
  
22 sem_remove($sem_id);
  
23
  
24
  
25 //可能出现的问题
  
26 $fp = sem_get(fileinode(__DIR__), 100);
  
27 sem_acquire($fp);
  
28
  
29 $fp2 = sem_get(fileinode(__DIR__), 1));
  
30 sem_acquire($fp2);
  

  注释的很详细了,不懂的还可以查看一下手册的介绍。那么我们现在就用信号量来修改我们的方法吧。
  

1 <?php  

2 /**  

3  * author: NickBai  

4  * createTime: 2016/12/5 0005 上午 10:26  

5  */  
6
  
7 //共享内存通信
  
8
  
9 //1、创建共享内存区域
  
10 $shm_key = ftok(__FILE__, 't');
  
11 $shm_id = shm_attach( $shm_key, 1024, 0655 );
  
12 const SHARE_KEY = 1;
  
13 $childList = [];
  
14
  
15 //加入信号量
  
16 $sem_id = ftok(__FILE__,'s');
  
17 $signal = sem_get( $sem_id );
  
18
  
19 //2、开3个进程 读写 该内存区域
  
20 for( $i = 0; $i < 3; $i++ ){
  
21
  
22     $pid = pcntl_fork();
  
23     if( $pid == -1 ){
  
24         exit('fork fail!' . PHP_EOL);
  
25     }else if( $pid == 0 ){
  
26
  
27         // 获得信号量
  
28         sem_acquire($signal);
  
29
  
30         //子进程从共享内存块中读取 写入值 +1 写回
  
31         if ( shm_has_var($shm_id, SHARE_KEY) ){
  
32             // 有值,加一
  
33             $count = shm_get_var($shm_id, SHARE_KEY);
  
34             $count ++;
  
35             //模拟业务处理逻辑延迟
  
36             $sec = rand( 1, 3 );
  
37             sleep($sec);
  
38
  
39             shm_put_var($shm_id, SHARE_KEY, $count);
  
40         }else{
  
41             // 无值,初始化
  
42             $count = 0;
  
43             //模拟业务处理逻辑延迟
  
44             $sec = rand( 1, 3 );
  
45             sleep($sec);
  
46
  
47             shm_put_var($shm_id, SHARE_KEY, $count);
  
48         }
  
49
  
50         echo "child process " . getmypid() . " is writing ! now count is $count\n";
  
51         // 用完释放
  
52         sem_release($signal);
  
53         exit( "child process " . getmypid() . " end!\n" );
  
54     }else{
  
55         $childList[$pid] = 1;
  
56     }
  
57 }
  
58
  
59 // 等待所有子进程结束
  
60 while( !empty( $childList ) ){
  
61     $childPid = pcntl_wait( $status );
  
62     if ( $childPid > 0 ){
  
63         unset( $childList[$childPid] );
  
64     }
  
65 }
  
66
  
67 //父进程读取共享内存中的值
  
68 $count = shm_get_var($shm_id, SHARE_KEY);
  
69 echo "final count is " . $count . PHP_EOL;
  
70
  
71
  
72 //3、去除内存共享区域
  
73 #从系统中移除
  
74 shm_remove($shm_id);
  
75 #关闭和共享内存的连接
  
76 shm_detach($shm_id);
  

  运行结果:
DSC0001.png

  完美的处理了进程之间抢资源的问题,实现了操作的原子性!
  参考文章:
  http://www.cnblogs.com/siqi/p/3999222.html
  http://www.cnblogs.com/siqi/p/3997444.html
  http://www.jianshu.com/p/08bcf724196b

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-430124-1-1.html 上篇帖子: PHP获取微信页面的指定内容 下篇帖子: PHP后台接收Base64图片
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表