进程间通信的历史与未来

- START -

我们都知道线程是共享内存空间的,因此不会发生所谓的通信,而进程则存在如何防止多进程同时访问数据的排他控制问题。

5 种进程间通信的方式

  • 管 道
  • SysV IPC
  • TCP 套接字
  • UDP 套接字
  • UNIX 套接字

管道

  所谓管道,就是能够从一侧输入,然后从另一侧读取的文件描述符对。Shell 中的管道也是通过这一方式实现的。

  文件描述符在每个进程中是独立存在的,但创建子进程时会继承父进程中所有的文件描述符,因此它可以用于在具有父子、兄弟关系的进程之间进行通信。

  例如,在具有父子关系的进程之间进行管道通信时,可以按下列步骤操作。在这里为了简单期间,我们只由子进程向父进程进行通信。

  1. 首先,使用 pipe 系统调用,创建一对文件描述符。下面我们将读取一方的文件描述符称为 r,将写入一侧的文件描述符称为 w
  2. 通过 fork 系统调用创建子进程。
  3. 在父进程一方将描述符 w 关闭。
  4. 在子进程一方将描述符 r 关闭。
  5. 在子进程一方将要发送给父进程的数据写入描述符 w
  6. 在父进程一方从描述符 r 中读取数据。

笔者直接上代码演示:

#!/usr/bin/env python
#coding:utf8
import multiprocessing
import time

def process_one(pipe):
    while True:        
        for i in range(3):            
        print "Process-one send: {0}".format(i)
            pipe.send(i)
            time.sleep(1)

def process_two(pipe):
    while True:        
        print "Process-two received: {0}".format(pipe.recv())
        time.sleep(1)
        
# 创建一个管道
pipe = multiprocessing.Pipe()
# pipe 对象:
# (<read-write Connection, handle 5>, <read-write Connection, handle 6>)


p1 = multiprocessing.Process(target=process_one, args=(pipe[0],))
p2 = multiprocessing.Process(target=process_two, args=(pipe[1],))

# 开始子进程
p1.start()
p2.start()

# 等待,直到子进程结束
p1.join()
p2.join()

结果

Process-one send: 0
Process-two received: 0
Process-one send: 1
Process-two received: 1
Process-one send: 2
Process-two received: 2
Process-one send: 0
Process-two received: 0
Process-one send: 1
Process-two received: 1
Process-one send: 2
Process-two received: 2
...

SysV IPC

  UNIX 的 System V (Five) 版本引入了一组称为 SysV IPC 的进程间通信 API,其中 IPC 就是 Inter Process Communication (进程间通信)的缩写。

SysV IPC 包括下列 3 种通信方式。

  • 消息队列
  • 信号量
  • 共享内存

  消息队列是一种用于进程间通信的手段。管道只是一种流机制,每次写入数据的长度等信息是无法保存的,相对的,消息队列则可以保存写入消息的长度。

  信号量(semaphore)是一种带有互斥计数器的标志(flag)。这个词原本是荷兰语「旗语」的意思,在信号量中可以设定对某种「资源」同时访问数量的上限。

  共享内存是一块在进程间共享的内存空间。通过将共享内存空间分配到自身进程内存空间中(attach)的方式来访问。由于对共享内存的访问并没有进行排他控制,因此无法避免一些偶发性问题,必须使用信号量等手段进行保护。

  不过,SysV IPC 有一个资源泄露的问题:由于 SysV IPC 的通信路径能够跨进程访问,因此在使用时需要向操作系统申请分配才能进行通信,通信结束之后还必须显式的销毁,如果忘记销毁的话,就会在操作系统中留下垃圾。

管道之类的方式,则在其所属进程结束的同时会自动销毁,因此比 SysV IPC 要更加易用。

  其次,学习使用新的 API 要花一些精力,但结果也只能用在一台电脑上的进程间通信中,真的让人没什么动力去学呢。

  大家可以在 Linux 中参考一下:

# man svipc

套接字

  System V 所提供的进程间通信手段是 SysV IPC,相对的,BSD 则提供了套接字的方式。和其他进程间通信方式相比,套接字有一些优点:

  • 通信对象不仅限于同一台计算机,或者说套接字本身主要就是为了计算机之间的通信而设计的。
  • (和 SysV IPC 不同)套接字也是一种文件描述符,可进行一般的输入输出。尤其是可以使用 select 系统调用,在通常 I/O 的同时进行「等待」,这一点非常方便。
  • 套接字在进程结束后会由操作系统自动释放,因此无需担心资源泄漏的问题。
  • 套接字(由于其优秀的设计)从很早开始就被吸收进 System V 等系统了,因此在可移植性方面的顾虑较少。

  现在网络几乎完全依赖于套接字。各位所使用的几乎所有的服务的通信都是基于套接字实现的,这样说应该没什么大问题。

套接字分很多种,其中具有代表性的包括:

  • TCP 套接字
  • UDP 套接字
  • UNIX 套接字

  TCP(Transmission Control Protocol,传输控制协议)套接字和 UDP(User Datagram Protocol,用户数据协议)套接字都是建立在 IP(Internet Protocol,网际协议)协议之上的上层网络通信套接字。这两种套接字都可用于以网络为媒介的结算机通信。但它们在性质上有一些区别。

  TCP 套接字是一种基于连接的、具备可靠性的数据流通信套接字。所谓基本连接,是指通信的双方是固定的;而所谓具备可靠性,是指能够侦测数据发送成功或是失败(出错)的状态。

  所谓数据流通信,是指发送的数据是作为字节流来处理的,和通常的输入输出一样,不会保存写入的数据长度信息。

  看了上面的内容,大家可能觉得这些都是理所当然的。我们和 UDP 套接字对比一下,就能够理解其中的区别了。

  UDP 套接字和 TCP 套接字相反,是一种能够无需连接进行通信、但不具备可靠性的数据通信套接字。所谓能够无需连接进行通信,是指无需固定连接到指定对象,可以直接发送数据;不具备可靠性是指可能会出现中途由于网络状况等因素导致发送数据丢失的情况。

  在数据通信中,发送的数据再原则上是能够保存其长度的。但是,在数据过长等情况下,发送的数据可能会被分割。

  先不说无连接通信这一点,UDP 和其他一些性质可能会让大家感到非常难用。这是因为 UDP 几乎是原原本本直接使用了作为其基础的 IP 协议。相反 TCP 为了维持可靠性,在 IP 协议之上构建了各种机制。UDP 的特点是结构简单,对系统产生的负荷也较小。

  因此,在语音通信(如 IP 电话等)中一般使用 UDP,因为通信的性能比数据传输的可靠性要更加重要,也就是说,相比通话中包含少许杂音来说,还是保证较小的通话延迟要更加重要。

  TCP 套接字和 UDP 套接字都是通过 IP 地址和端口号来进行工作的。例如,http 协议中的 http://www.google.com:80/ 就表示与 www.google.com (IP 地址为:31.13.71.7)所代表的计算机的 80 端口建立连接.

UNIX 套接字

  同样是套接字,UNIX 套接字和 TCP、UDP 套接字相比,可以算是一个异类。基于 IP 的套接字一般是通过主机名端口号来识别通信对象的,而 UNIX 套接字则是在 UNIX 文件系统上创建一个特殊文件,并用该文件的路径进行识别。由于这种方式使用的是文件系统,因此大家可以看出,UNIX 套接字只能用于同一台计算机上的进程间通信。

  UNIX 套接字并不是基于 IP 的套接字,它可用于向一台计算机上其他进程提供服务的某种服务程序。

最后

  在进程通信手段中,套接字算是非常好用的,但是即便如此,在考虑对工作进行「委派」时,其易用性还并不理想。套接字本来是为网络服务器的实现而设计的,但作为构建分布式应用程序的手段来说,还是太原始了。

MQ(Message Queue,消息队列)就是为了解决这个问题而诞生的,RPC(Remote Procedure Call,远程过程调用)呢,这一切有没有联系呢?希望本文可以引发你的思考

原文发布于微信公众号 - 随心DevOps(heart-devops)

原文发表时间:2017-12-28

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏服务端技术杂谈

Service Mesh优化

Provider负载均衡:加权轮训,最小响应时间Tcp连接负载均衡:支持按最小请求选择Tcp连接Dubbo请求:批量encodeTcp参数优化:开启TCP_N...

832
来自专栏王亚昌的专栏

vmstat命令详解与举例

[root@localhost ~]# vmstat -n 3       (每个3秒刷新一次) procs -----------memory-------...

591
来自专栏专注 Java 基础分享

完整的一次 HTTP 请求响应过程(一)

因特网无疑是人类有史以来最伟大的设计,它互联了全球数亿台计算机、通讯设备,即便位于地球两端的用户也可在顷刻间完成通讯。

1050
来自专栏CSDN技术头条

NoSQL数据库的主主备份

Tarantool DBMS的高性能应该很多人都听说过,包括其丰富的工具套件和某些特定功能。比如,它拥有一个非常强大的on-disk存储引擎Vinyl,并且知道...

19010
来自专栏前端架构

分析HTTP,TCP的长连接和短连接以及sock

HTTP的长连接和短连接本质上是TCP长连接和短连接。HTTP属于应用层协议,在传输层使用TCP协议,在网络层使用IP协议。IP协议主要解决网络路由和寻址问题,...

722
来自专栏前沿技墅

服务化的基石:聊聊通信协议那些事儿

1597
来自专栏美团技术团队

Java NIO浅析

NIO(Non-blocking I/O,在Java领域,也称为New I/O),是一种同步非阻塞的I/O模型,也是I/O多路复用的基础,已经被越来越多地应用到...

3599
来自专栏IT技术精选文摘

负载均衡详解

面对大量用户访问、高并发请求,海量数据,可以使用高性能的服务器、大型数据库,存储设备,高性能Web服务器,采用高效率的编程语言比如(Go,Scala)等,当单机...

2027
来自专栏北京马哥教育

Linux性能及调优指南(翻译)之Linux进程管理

本文为IBM RedBook的Linux Performanceand Tuning Guidelines的1.1节的翻译 原文地址:http://www.re...

3319
来自专栏python3

python3--进程

进程的概念起源于操作系统,是操作系统最核心的概念,也是操作系统提供的最古老也是最重要的抽象概念之一。操作系统的其他所有内容都是围绕进程的概念展开的

492

扫描关注云+社区