/首页
/开源
/关于
PHP进程通信之共享内存+UNIX Socket(二十四节)
发表@2020-03-08 13:35:58
更新@2023-04-27 15:40:33
大家好,老李是我;我永远都爱这样的我。  有人跟我说:「老李,你再也不是以前的你了」。他说这句话的时候,我仿佛感觉到了当年马克·查普曼在一枪干掉了约翰·列侬后,对着列侬的尸体说:“ 你变了 ”... 我怎么能变呢,我还是那个老李:就算整个晋西北乱成了一锅粥,我还是我,是颜色不一样的烟火,当年那个十里八乡有名的俊后生,当年雪上草地里背过锅,二营长意大利炮扛上来,老子打得就是他精锐,别说它一个板田联队,有一个师老子就TM敢打太原。你好好看看我哪儿变了?   硬核,风骚,还带着那么一丝丝小帅。就算说变,那也仅仅可能是在向「真*谢顶道人」的方向变,真到那个时候我就改号为「秃顶法师」。 恒河水一口下了肚,今天要搞一波儿流。还是有同学整不明白进程间通信的意义啊,结合WM简单说一种情况,众所周知WM的进程模型是Master-Worker类型的,具体表现大概就是Master fork出Worker,每个Worker持有一个Event-Loop,这是一种与Nginx十分相似的进程模型,但我估计不少老哥平时用WM没准能开大几十甚至上百个Worker进程,虽然用起来是没问题,但总归很奇怪,你见过Nginx进程开这么多的么(你要说你服务器是128核那当我没说)?所以李亮在WM问答社区里推荐一种做法就是再在WM后面接入一群任务服务器,前后之间利用异步方式通信,大概就是类似于挡在前面这一坨WM服务器负责高吞吐数据转发,业务逻辑的处理则是交给后面的一坨任务服务器。其实吧,用过Swoole多进程模式的铁子们应该比较熟悉,其实就是Swoole里Worker进程与Tasker进程的故事。 上面叨逼叨这么一堆,你应该意识到如果WM自己本身带一组Task进程也挺好的,Worker进程就可以靠高性能的进程间通信与Task进程数据交互。 在关于进程间通信这里,一牵涉到共享内存就脑袋大,多个进程/线程访问同一块儿共享内存,你就是抠脚趾头都能预料到读写锁的问题,所以还得有个信号量来「相濡以沫」一下: 一个是坦克,另一个是坦克行车记录仪 一个是航母,另一个是航母雨刮器 一个是潜艇,另一个是潜艇风湿活血理疗仪 一个是飞机,另一个是飞机耐磨防刮漆 也就是说信号量和共享内存是分不开的,要用也是搭配着用。*NIX的一些书籍中甚至不建议新手轻易使用这种进程间通信的方式,因为这是一种极易产生死锁的解决方案。共享内存顾名思义,就是一坨内存中的区域,可以让多个进程进行读写。为了解决这个问题才引入了信号量,信号量是一个计数器,是配合共享内存使用的,一般情况下流程如下: - 当前进程获取将使用的共享内存的信号量 - 如果信号量大于0,那么就表示这块儿共享资源可以使用,然后进程将信号量减1 - 如果信号量为0,则进程进入休眠状态一直到信号量大于0,进程唤醒开始从1一个进程不再使用当前共享资源情况下,就会将信号量减1。 这个地方,信号量的检测并且减1是原子性的,也就说两个操作必须一起成功,这是由系统内核来实现的。在PHP中,信号量和共享内存先后一共也就这几个函数:  其中sem前缀的是信号操作相关函数,shm前缀是共享内存相关函数。 ```php 0 ) { $child_pid[] = $pid; } } while( !empty( $child_pid ) ){ foreach( $child_pid as $pid_key => $pid_item ){ pcntl_waitpid( $pid_item, $status, WNOHANG ); unset( $child_pid[ $pid_key ] ); } } // 休眠2秒钟,2个子进程都执行完毕了 sleep( 2 ); echo '最终结果'.shm_get_var( $shm_id, SHM_VAR ).PHP_EOL; // 记得删除共享内存数据,删除共享内存是有顺序的,先remove后detach,顺序反过来php可能会报错 shm_remove( $shm_id ); shm_detach( $shm_id ); ``` 然而,今天压箱底的是AF_UNIX所代表的UNIX本地socket方式。当然了人家是先有socket后有这种UNIX Socket,这玩意也是后来满满发展来的,你可以理解为一开始socket是面向跨机器的网络通信,后来发现这玩意纯用在本地搞搞单机版跨进程通信效果也贼不错,而且这种本地版本的socket跑在127.0.0.1地址上,你别看是socket然而实际上一不经过网卡、二也没有网络协议解析那些乱七八糟的,这是一种可靠的连接服务。 其实有些老哥在折腾MySQL的时候应该注意到了,一个叫做mysql.sock的文件;或者折腾php-fpm与Nginx的时候,有个php-fpm.sock。一般约定俗成的话,这种后缀为sock的文件就是UNIX本地socket。 拿php-fpm里这个sock来说,当你把Nginx服务器与php-fpm部署在同一台机器上的时候,你完全可以考虑使用本地socket的方式让Nginx与php-fpm进行数据交换,很明显这种省略掉网络开销的通信方式应该是更高效的,不过我曾经在网上看到过一个中论调,大概是说「php-fpm这种unix socket通信方式不稳定」而且还感染了相当一批人,但是通篇也没有看到这种论调的论据是什么。如果你知道这种「不稳定」的原因,可以后台提供一下,我下篇文章打补丁。 由于前面我们说过socket相关的操作函数,所以下面的demo你们看起来应该是不费吹灰之力的,但是你们一定要把注意力分配到注释上: ```php /*********** 一侧代码 **********/