程序员潇然 发表于 2022-7-17 15:42:07

进程通信概念简介 多线程上篇(六)

进程通信指的是进程间的信息交换 ,IPC(Inter-Process Communication,进程间通信)

之前说到:
进程通信就相当于一种工作方式、沟通形式,比如你给我一个SVN标签号并且告知我意图,我去库中检索指定标签修改的指定内容,就完成了一个任务的协作。

进程同步中,也有信息的交换,比如互斥量访问,再比如生产者和消费者,共享缓冲池,但是这些通常被称之为低级的进程通信。
以信号量为例,如果你说我在塔顶点亮灯表示危险,否则表示安全,这没问题,如果你想用灯亮灯灭来像QQ一样,大段大段的传递讯息可能么?
多搞几盏灯?然后用编码?即使那样你依然是很费力的。

所以说尽管比如信号量机制作为同步工具是卓有成效的,但作为通信工具,则不够方便
首先是效率低,另外共享数据结构的设置、数据的传送、进程的互斥与同步等,都必须由程序员自己去实现
所以,对于进程间的通信,我们迫切的渴望有“封装好的方法”
进程通信主要指的就是操作系统提供的进程通信工具(“封装好的方法”)用来进程间的信息交换。

### 进程通信类型

主要有四种通信方式
!(data/attachment/forum/202207/17/153440gvgevyy2gisymsh2.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")

### 共享存储器系统

共享存储系统关键在于共享
主要两种:

* 共享数据结构
* 共享存储区

!(data/attachment/forum/202207/17/153512l2qz0lte8qe2tm28.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")
`本文作者:程序员潇然 疯狂的字节X https://crazybytex.com/`

### 管道通信

这种方式首创于UNIX 系统,由于它能有效地传送大量数据,因而又被引入到许多其它的操作系统中
管道指用于连接一个读进程和一个写进程以实现它们之间通信的一个共享文件,这个文件又被叫做pipe文件
管道可以理解为一种比较特殊的“共享存储器系统”的通信方式
!(data/attachment/forum/202207/17/153535gilrklllg1lueilk.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")

### 消息传递系统

在该机制中,进程不必借助任何共享存储区或数据结构,而是以格式化的消息(message)为单位,将通信的数据封装在消息中
并利用操作系统提供的一组通信命令(原语),在进程间进行消息传递,完成进程间的数据交换
计算机网络中的报文就是一种message
!(data/attachment/forum/202207/17/153607f61npjpu61rz4uz7.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")

消息传递系统中的消息,其实也是“共享数据结构”的一种形式,是一种特殊的形式

因为被共享的数据结构而不是随便自定义的,而是大家共同设计好的,认可的,一致的一种格式。
基于这种一致性的格式,然后操作系统对他提供了支持
所以,他就是共享数据结构的一个特例,这个特例使用更加方便简单。
简单说,计算机报文是一种message,那么操作系统对整个TCP/IP协议栈的实现,这就是通信命令,封装了底层的传输细节,对开发者来说是透明的

### 客户服务器系统

客户机一服务器系统的通信机制,在网络环境的各种应用领域已成为当前主流的通信实现机制
BS架构的系统本质也是包装演化的CS,浏览器难道不是客户端软件么
主要有三种:

* 套接字
* 远程过程调用
* 远程方法调用

> 远程过程调用 RPC(Remote Procedure Call),RPC采用客户机/服务器模式,请求程序就是一个客户机,而服务提供程序就是一个服务器
> RPC允许你 透明的 调用远程服务器上提供的服务
> 对于面向对象的编程中,远程过程调用通常也被称为远程方法调用

比如java1.1中实现的Remote Method Invocation,RMI,可以认为是RPC的Java版本,RMI大大增强了Java开发分布式应用的能力。

概括地说的话:
远程过程调用RPC是一种协议概念,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。
RPC不依赖于具体的网络传输协议,tcp、udp等都可以。

协议必然需要被实现才能使用,他只是一种协议与标准,RMI就相当于是sun对RPC的一个Java实现

Socket是应用层与TCP/IP协议族通信的中间软件抽象层,是更加底层的通信工具,所以可以认为RPC是socket的上层建筑
一个或许不是很合适的比喻:如果RPC是议论文格式,那么RMI可能是汉语的议论文,socket可能是汉字
!(data/attachment/forum/202207/17/153730nj8aa3uq503aqt57.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")

#### 套接字原理步骤

双方进程运行在不同主机的网络环境下,被分配了一对套接字,一个属于接收进程(或服务器端),一个属于发送进程(或客户端)。
发送进程(或客户端)发出连接请求时,随机申请一个套接字,主机为之分配一个端口,与该套接字绑定,不再分配给其它进程。
接收进程(或服务器端)拥有全局公认的套接字和指定的端口(如ftp服务器监听端口为21,Web或http服务器监听端口为80),并通过监听端口等待客户请求。因此,任何进程都可以向它发出连接请求和信息请求,以方便进程之间通信连接的建立。
接收进程(或服务器端)一旦收到请求,就接受来自发送进程(或客户端)的连接,完成连接,即在主机间传输的数据可以准确地发送到通信进程,实现进程间的通信;
当通信结束时,系统通过关闭接收进程(或服务器端)的套接字撤销连接。

#### 远程过程原理步骤

负责RPC的调用的进程有两个,一个是本地客户进程,另一个是远程服务进程,这两个进程也被称之为网络守护进程,主要负责网络之间的消息传递
通常处于阻塞之中,等待消息。
而远程过程调用的透明化核心就是所谓的存根,也叫做桩stub
所以说两个进程和两个存根是RPC的根本

#### 桩的原理

!(data/attachment/forum/202207/17/153810wnps55zh0jlbw5bl.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")
**桩类似代理者、转换器一类的角色**

举一个例子:
在一些与底层硬件芯片等交互的软件层,软件层依赖底层芯片的处理,如何更方便的进行代码测试?
也就是相当于A类中有方法functionA,B类中有方法functionB(假定functionB 是硬件处理部分,测试并不方便,需要依赖设备)

```
functionA(){
functionB();
}
```

我们就可以编写一个新的类C,提供桩方法:functionStubC,所有调用functionB的地方全部使用functionStubC,也就是

```
functionA(){
functionStubC();
}
```

而functionStubC的返回值,永远是functionB成功的返回值。
这就是一个打桩的概念。

**RPC也是通过这个桩的概念来实现透明的远程过程调用**
对本地调用来说,他看到的只是客户端存根(stub)
就如同我们上面的functionStubC,当然比functionStubC要更加复杂,所有的处理都在他背后被执行
所以对本地调用来说是透明的
!(data/attachment/forum/202207/17/153915gg1d116l01dzdavr.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")

#### RPC过程步骤

!(data/attachment/forum/202207/17/153938ugdonoubcjfadoa5.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")

1. 本地过程调用者以一般方式调用远程过程在本地关联的客户存根,传递相应的参数,然后将控制权转移给客户存根;
2. 客户存根执行,完成包括过程名和调用参数等信息的消息建立,将控制权转移给本地客户进程;
3. 本地客户进程完成与服务器的消息传递,将消息发送到远程服务器进程;
4. 远程服务器进程接收消息后转入执行,并根据其中的远程过程名找到对应的服务器存根,将消息转给该存根;
5. 该服务器存根接到消息后,由阻塞状态转入执行状态,拆开消息从中取出过程调用的参数,然后以一般方式调用服务器上关联的过程;
6. 在服务器端的远程过程运行完毕后,将结果返回给与之关联的服务器存根;
7. 该服务器存根获得控制权运行,将结果打包为消息,并将控制权转移给远程服务器进程:
8. 远程服务器进程将消息发送回客户端;
9. 本地客户进程接收到消息后,根据其中的过程名将消息存入关联的客户存根,再将控制权转移给客户存根;
10. 客户存根从消息中取出结果,返回给本地调用者进程,并完成控制权的转移。

### 总结

进程通信的一些核心思想与基本形式就是上面的这些简介
这些是最纯粹基本的理论知识,而对于我们实际的开发中,面对的总是各种各样的对于现存的理论的实现版本
核心仍旧是依赖操作系统以及语言本身的实现以及一些框架等
但是理解这些概念是必要的,能让你后续的学习路线越来越明朗,技术是层不出穷的,你仅仅学习招式,永远也无法穷尽,还导致自己没有底蕴。

!(data/attachment/forum/202206/16/141330jha7st9soow8772i.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "common_log.png")
`转载务必注明出处:程序员潇然,疯狂的字节X,https://crazybytex.com/thread-59-1-1.html `
页: [1]
查看完整版本: 进程通信概念简介 多线程上篇(六)