当前位置:首页 > PHP教程 > php高级应用 > 列表

PHP进程间通信的几种方法详解

发布:smiling 来源: PHP粉丝网  添加日期:2023-09-16 16:24:08 浏览: 评论:0 

PHP是用C编写的,因此它对系统底层API的操作与C很像,同大多数语言一样,PHP进程间通信的方式有以下几种:消息队列,管道,共享内存,socket和信号,本文是对这几种通信方式对整理。

管道通信PIPE

管道用于承载简称之间的通讯数据。为了方便理解,可以将管道比作文件,进程A将数据写到管道P中,然后进程B从管道P中读取数据。php提供的管道操作API与操作文件的API基本一样,除了创建管道使用posix_mkfifo函数,读写等操作均与文件操作函数相同。当然,你可以直接使用文件模拟管道,但是那样无法使用管道的特性了。

通过管道通信的大概思路是,首先创建一个管道,然后子进程向管道中写入信息,父进程从管道中读取信息,这样就可以做到父子进程直接实现通信了。

  1. <?php 
  2. // 创建管道 
  3. $pipePath = "pipe"
  4. if( !file_exists$pipePath ) ){ 
  5.     if( !posix_mkfifo( $pipePath, 0666) ){ 
  6.         exit('make pipe false!' . PHP_EOL); 
  7.     } 
  8. // 创建进程,子进程写管道,父进程读管道 
  9. // 通过 pcntl_fork函数创建一个子进程。 
  10. // pcntl_fork 函数 很特殊,它调用一次拥有 多个返回值。 
  11. // 在父进程中:它返回 子进程的ID 这个值是 大于0 的。 
  12. // 在子进程中,它返回0。当返回 -1 时表示创建进程失败。 
  13. $pid = pcntl_fork(); 
  14. if$pid == 0 ){ 
  15.     // 子进程写管道 
  16.     $file = fopen$pipePath'w'); 
  17.     fwrite( $file'hello world'); 
  18.     sleep(1); 
  19.     exit
  20. }else
  21.     // 父进程读管道 
  22.     $file = fopen$pipePath'r'); 
  23.     // 设置成读取非阻塞 
  24.     // 当读取是非阻塞的情况下,父进程进行读取信息的时候不会等待, 
  25.     // 管道中没有消息也会立马返回。 
  26.     // stream_set_blocking( $file, False);  
  27.     echo fread$file, 20) . PHP_EOL; 
  28.     pcntl_wait($status); // 回收子进程 

消息队列

消息队列是存放在内存中的一种队列数据结构。

  1. <?php 
  2. // 获取父进程id 
  3. $parentPid = posix_getpid(); 
  4. echo "parent progress pid:{$parentPid}\n"
  5. $childList = array(); 
  6. // 创建消息队列,定义消息类型 
  7. $id = ftok(__FILE__'m'); 
  8. $msgQueue = msg_get_queue($id); 
  9. const MSG_TYEP = 1; 
  10. // 生产者 
  11. function producer() 
  12.     global $msgQueue
  13.     $pid = posix_getpid(); 
  14.     $repeatNum = 5; 
  15.     for ($i = 0; $i <= $repeatNum$i++) { 
  16.         $str = "({$pid}) progress create! {$i}"
  17.         msg_send($msgQueue, MSG_TYEP, $str); 
  18.         $rand = rand(1, 3); 
  19.         sleep($rand); 
  20.     } 
  21. // 消费者 
  22. function consumer() 
  23.     global $msgQueue
  24.     $pid = posix_getpid(); 
  25.     $repeatNum = 6; 
  26.     for ($i = 1; $i<= $repeatNum$i++) { 
  27.         $rel = msg_receive($msgQueue, MSG_TYEP, $msgType, 1024, $message); 
  28.         echo "{$message} | consumer({$pid}) destroy \n"
  29.         $rand = rand(1, 3); 
  30.         sleep($rand); 
  31.     } 
  32. function createProgress($callback
  33.     $pid = pcntl_fork(); 
  34.     if ($pid == -1) { 
  35.         // 创建失败 
  36.         exit("fork progresses error\n"); 
  37.     } elseif ($pid == 0) { 
  38.         // 子进程执行程序 
  39.         $pid = posix_getpid(); 
  40.         $callback(); 
  41.         exit("({$pid})child progress end!\n"); 
  42.     } else { 
  43.         // 父进程 
  44.         return $pid
  45.     } 
  46. for ($i = 0; $i < 3; $i++) { 
  47.     $pid = createProgress('producer'); 
  48.     $childList[$pid] = 1; 
  49.     echo "create producer progresses: {$pid}\n"
  50. for ($i = 0; $i < 2; $i++) { 
  51.     $pid = createProgress('consumer'); 
  52.     $childList[$pid] = 1; 
  53.     echo "create consumer progresses: {$pid}\n"
  54. while (!emptyempty($childList)) { 
  55.     $childPid = pcntl_wait($status); 
  56.     if ($childPid > 0) { 
  57.         unset($childList[$childPid]); 
  58.     } 
  59. echo "({$parentPid})main progress end!\n"

运行结果:

  1. create producer progresses: 21432 
  2.  
  3. create producer progresses: 21433 
  4. create producer progresses: 21434 
  5. create consumer progresses: 21435 
  6. (21426) progress create! 2 | consumer(21435) destroy 
  7. (21424) progress create! 1 | consumer(21436) destroy 
  8. create consumer progresses: 21436 
  9. (21426) progress create! 3 | consumer(21436) destroy 
  10. (21426) progress create! 4 | consumer(21435) destroy 
  11. (21425) progress create! 3 | consumer(21436) destroy 
  12. (21424) progress create! 2 | consumer(21435) destroy 
  13. (21426) progress create! 5 | consumer(21435) destroy 
  14. (21424) progress create! 3 | consumer(21436) destroy 
  15. (21433)child progress end
  16. (21425) progress create! 4 | consumer(21435) destroy 
  17. (21424) progress create! 4 | consumer(21436) destroy 
  18. (21434)child progress end
  19. (21424) progress create! 5 | consumer(21435) destroy 
  20. (21425) progress create! 5 | consumer(21436) destroy 
  21. (21432)child progress end
  22. (21435)child progress end
  23. (21436)child progress end
  24. (21431)main progress end

信号量与共享内存

  1. <?php 
  2. $parentPid = posix_getpid(); 
  3. echo "parent progress pid:{$parentPid}\n"
  4. // 创建共享内存,创建信号量,定义共享key 
  5. // ftok(文件路径,资源标识符) 创建一个IPC通信所需的id 
  6. $shm_id = ftok(__FILE__'m'); 
  7. $shm_id = ftok(__FILE__'s'); 
  8. // shm_attach(id) 创建或者打开一个共享内存 
  9. $shareMemory = shm_attach($shm_id); 
  10. // 返回一个可用户访问系统信号量的id 
  11. $signal = sem_get($shm_id); 
  12. const SHARE_KEY = 1; 
  13. // 生产者 
  14. function producer() { 
  15.     global $shareMemory
  16.     global $signal
  17.     $pid = posix_getpid(); 
  18.     $repeatNum = 5; 
  19.     for ($i = 1; $i <= $repeatNum$i++) { 
  20.         // 获得信号量 - 阻塞进程,直到信号量被获取到[lock锁机制的关键] 
  21.         sem_acquire($signal); 
  22.         // 检查某个key是否存在与共享内存中 
  23.         if (shm_has_var($shareMemory, SHARE_KEY)) { 
  24.             // 获取共享内存中的key的值 
  25.             $count = shm_get_var($shareMemory, SHARE_KEY); 
  26.             $count ++; 
  27.             // 为共享内存中的key赋值 
  28.             shm_put_var($shareMemory, SHARE_KEY, $count); 
  29.             echo "({$pid}) count: {$count}\n"
  30.         } else { 
  31.             // 初始化 
  32.             shm_put_var($shareMemory, SHARE_KEY, 0); 
  33.             echo "({$pid}) count: 0\n"
  34.         } 
  35.         // 释放 
  36.         sem_release($signal); 
  37.     } 
  38. function createProgress($callback) { 
  39.     $pid = pcntl_fork(); 
  40.     if ($pid == -1) { 
  41.         // 创建失败 
  42.         exit("fork progress error!\n"); 
  43.     } elseif ($pid == 0) { 
  44.         // 子进程 
  45.         $pid = posix_getpid(); 
  46.         $callback(); 
  47.         exit("({$pid}) child progress end!\n"); 
  48.     } else { 
  49.         // 父进程 
  50.         return $pid
  51.     } 
  52. // 3个写进程 
  53. for ($i = 0; $i < 3; $i ++) { 
  54.     $pid = createProgress('producer'); 
  55.     $childList[$pid] = 1; 
  56.     echo "create producer child progress: {$pid} \n"
  57. // 等待所有子进程 
  58. while (!emptyempty($childList)) { 
  59.     $childPid = pcntl_wait($status); 
  60.     if ($childPid > 0) { 
  61.         unset($childList[$childPid]); 
  62.     } 
  63. // 释放共享内存与信号量 
  64. shm_remove($shareMemory); 
  65. sem_remove($signal); 
  66. echo "({$parentPid}) main progress end!\n"

运行结果:

使用信号量来实现共享内存的锁机制

  1. parent progress pid:31720 
  2. create producer child progress: 31721  
  3. create producer child progress: 31722  
  4. (31721) count: 0 
  5. (31721) count: 1 
  6. (31721) count: 2 
  7. (31721) count: 3 
  8. (31721) count: 4 
  9. (31721) child progress end! 
  10. create producer child progress: 31723  
  11. (31722) count: 5 
  12. (31722) count: 6 
  13. (31722) count: 7 
  14. (31722) count: 8 
  15. (31722) count: 9 
  16. (31722) child progress end! 
  17. (31723) count: 10 
  18. (31723) count: 11 
  19. (31723) count: 12 
  20. (31723) count: 13 
  21. (31723) count: 14 
  22. (31723) child progress end! 
  23. (31720) main progress end! 

无锁情况

Warning: sem_release(): SysV semaphore 4357894312 (key 0x73048925) is not currently acquired in /Users/easyboom/www/example/信号量与共享内存.php on line 38

Tags: PHP进程间通信

分享到: