第43章 进程间通信简介

本文介绍了UNIX系统中进程间通信(IPC)的各种工具,包括管道、FIFO、socket、消息队列、共享内存等,并探讨了同步工具如信号量和文件锁。这些工具用于数据交换和协调进程操作,各有优缺点,适用于不同的场景和需求。文章还提到了工具的持久性、可移植性和性能等因素在选择IPC工具时的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        本章将对进程和线程之间用来相互通信和同步的工具进行一个简要的介绍,下面的章节将会升入介绍这些工具的细节信息。

        43.1 IPC工具分类

        图43-1总结了UNIX 系统上各种通信和同步工具,并根据功能将他们呢分成了三类。

  • 通信:这些工具关注进程之间的数据交换。
  • 同步:这些进程关注进程和线程操作之间的同步。
  • 信号:尽管信号的主要作用并不在此,但在特定场景下仍然可以将它作为一种同步技术。更罕见的的是信号还可以作为一种通信技术:信号编号本身是一种形式的信息,并且可以在实时信号上绑定数据(一个整数或指针)。

        尽管其中一些工具关注的是同步,但通用术语进程间通信(IPC)通常指代所有这些工具。

        从43-1中可以看出,通常几个工具会提供类似的IPC功能,之所以胡这样手机处于下列原因。

  • 不同的工具在不同的UNIX 实现上各自进行演化,随后被移植到了其他UNIX系统上。如FIFO首先是在System V上实现的,而(流)socket是首先是在BSD上实现的。
  • 新工具被开发出来用于弥补之前剋四的工具存在的不足。如POSIX IPC 工具是对较早的System V IPC 工具的改进。

        图43-1中被分成一组的工具在一些场景中会提供完全不同的功能。如流Socket可以用来在网络上通信,而FIFO则只能用来在同一机器上的进程间进行通信。

 43.2通信工具

        图43-1中列出的各种通信工具允许进程间相互交换数据。(这些工具还可以用来在同一个进程中不同线程之间交换数据,但很少需要这样做,因为线程之间通过共享全局变量来交换信息。)

        可以将通信工具分成两类。

  • 数据传输工具:区分这些工具的关键因素是写入和读取的概念。为了进行通信一个进程将数据写入到IPC工具中,另一个进程从中读取数据。这些工具要求在用户内存和内核内存之间进行两次数据传输:一次是再写入的时候从用户内存写入的时候从用户到内核内存,另一次传输是在读取的时候从内核内存到用户内存。(图43-2展示了管道在这一场景的用法)
  • 共享内存:共享内存允许进程通过将数据放到有进程间共享的一块内存中已完成信息的交换。(内核通过将每个进程中的页表条目指向同一个RAM分页来实现这一功能,如图49.2所示。)一个进程可以通过将数据放到共享内存块中使得其他进程读取这些数据。由于通信无需系统调用亦即用户内存和内核内存之间的数据传输,因此共享内存的速度非常快。

数据传输

         可以进一步将数据传输工具分为下列类别。

  • 字节流:通过管道、FIFO亦即数据包socket交换的数据是一个无分隔符的字节流。每个读取操作可能会从IPC工具中读取任意数量的字节,不管写者写入的块的大小是什么。这个模型参考了传统的UNIX “文件是一个字节序列”模型。
  • 消息:通过System V消息队列、POSIX消息队列以及数据包socket交换的数据是以分隔符分割的消息。每个读取操作读取由写者写入的一整条消息,无法只读取部分消息,二八剩余部分留在IPC工具中,也无法在一个操作中读取多条消息。
  • 伪终端:伪终端是一种在特殊情况下使用的通信工具,64章会介绍

        数据储传输工具和共享内存之间的差别包括以下几个方面。

  • 尽管一个数据传输工具可能会有多个读取者,但读操作是具有破坏性的。读取操作会消耗数,其他进程将无法获取所消耗的数据。
  • 读取者和写者进程之间的同步是原子的。如果一个读取者试图从一个当前不包含数据的数据传输工具中,那么在默认情况下读取操作会阻塞直至一些进程向该工具写入了数据。

共享内存

        大多数现代UNIX系统提供了三种形式的共享内存:System V共享内存、POSIX共享内存以及内存映射。

        下面是使用共享内存时的注意点

  • 尽管共享内存的通信速度更快,但是速度上的优势使用来弥补需要在共享内存上发生的操作进行同步的不足的。如当一个进程正在更新共享内存中的一个数据时,另一个进程就不应该读取这个数据结构。在共享内存中,信号量通常用来作为同步方法。
  • 放入共享内存的数据对所有共享这块内存的进程可见。(这与上面数据传输工具中介绍的破坏性读取语义不同。)

43.3 同步工具

        通过图43-1中的同步工具可以协调进程的操作。通过同步可以防止进程执行诸如同时更新一块共享内存或同时更新文件的同一个数据块之类的操作。如果没有同步,那么这种同事更新的操作可能会导致应用程序产生作物的结果。

        UNIX系统提供了下列同步工具。

  • 信号量:一个信号量是一个由内核维护的整数,其值永远不会小于0.一个进程可以增加或减少一个信号量的值。如果一个进程试图将信号量的值减小到小于0,那么内核阻塞该操作直至信号量的值增长至允许执行该操作的程度。(或者进程可以要求执行一个非阻塞操作,那么就不会发生阻塞,内核会让该操作立即返回并返回一个标示无法立即执行该操作的错误。)信号量的含义是由应用程序来确定的。一个进程减小一个信号量(如从1到0)是为了预约某些公共资源的独占访问,在完成了资源访问之后可以增加信号量来释放共享资源以供其他进程使用,最常见的信号量是二元信号量--一个值只能是0或1的信号量,单处理一类共享资源拥有多个实例的应用程序需要使用最大值等于共享资源数量的信号量。Linux既提供了System V信号量,又提供了POSIX信号量,他们的功能是类似的。
  • 文件锁:文件锁是设计用来协调操作同一文件的多个进程的一种同步方法。他也可以来协调对其他共享资源的访问。文件锁分为两类:读(共享)锁和写(互斥)锁。任意进程都可以共享同一文件(或一个文件的某一段区域)的读锁,但当一个进程持有了一个文件(或文件区域)的写锁之后,其他进程将无法获取该文件(或文件区域)上的写锁。Linux通过flock()和fcntl()系统调用来提供文件加锁工具。flock()系统调用提供了一种简单的加锁机制,允许进程将一个共享或互斥锁加到整个文件上。由于功能有限。,现在已经很少使用flock()这个加锁工具了。fcntl()系统调用提供了记录枷锁,允许进程在同一文件的不同区域加上多个读锁和写锁。
  • 互斥体和条件变量:这些同步工具通常用于POSIX线程

        在执行进程间同步时通常需要根据功能需求来选择工具。当协调对文件的访问时文件记录枷锁通常是最佳的选择,而对于协调对其他共享资源的访问来讲,信号量通常是最佳的选择。

        通信工具也可以用来进行同步。如在44.3节中使用了一个管道laitongbu-父进程与子进程的动作。一般来讲,所有数据传输工作都可以用来同步,只是同步擦欧总是通过在工具中交换信息来完成的。

43.4 IPC工具比较

        在需要使用IPC时会发现有很多选择,读者在一开始可能会对这些选择感到迷惑。在后面介绍各个IPC工具的章节会把每个工具与其他类似的工具进行比较。下面介绍在确定选择何种IPC 工具时通常需要考虑的事项。

 IPC 对象标识和打开对象的句柄

        要打开一个IPC对象,进程必须要通过某种方式来标识出该对象,一旦将对象打开之后,进程必须要使用某种句柄来引用打开着的对象。表43-1对各种类型的IPC工具属性进行了总结。

   功能

        各种IPC工具在功能上是存在差异的,因此在确定使用何种工具时需要考虑这些差异。下面首先对数据传输工具和共享内存之间的差异进行总结。

  • 数据传输工具提供了读取和写入操作,传输的数据只提供一个读者进程消耗。内核会自动处理读者和写者之间的流控以及同步(这样当读者试图从当前为空的工具中读取数据时将会阻塞)。在很多应用程序设计中,这个模型都表现的很好。
  • 其他应用程序设计则更适合采用共享内存的方式。一个进程通过共享内存能够使数据对共享同一内存区域的所有进程可见。通信“擦欧总”是比较简单的---进程可以像访问自己的虚拟地址空间中的内存那样访问共享内存中的数据。另一方面,同步处理(可能还会有流控)会增加共享内存设计的复杂性。在需要维护共享状态(如共享数据结构)的应用程序中,这个模型表现的很好

        关于各种数据传输工具,下面几点是值得注意的。

  • 一些数据传输工具以字节流的形式传输数据(管道、FIFO以及流socket),另一些则是面向消息的(消息队列和数据包socket)。到底选择何种方法需要依赖于应用程序。(应用程序也可以在一个字节流工具上应用面向消息的模型,这可以通过使用分隔符、固定长度的消息,或对整条消息长度进行编码的消息头来实现)。
  • 与其他数据传输工具相比,System V 和POSIX 消息队列特有的一个特性是他们能够给消息赋一个数值类型或优先级,这样递送消息的顺序就可以与发送消息的顺序不同了。
  • 管道、FIFO、以及socket是使用文件描述符来实现的。这些IPC工具都支持63章中介绍的一组I/O模型:I/O多路复用(select()和poll()系统调用)、信号驱动的I/O以及Linux特有的epollAPI。这些技术的主要优势在于他们允许应用程序同时监控多个文件描述符以判断是否可以在某些文件描述符上执行I/O操作。与之相比,System V 消息队列没有使用文件描述符,因此不支持这些技术
  • POSIX 消息队列提供了一个通知工具,当一条消息进入了一个之前称之为空的队列中时可以使用他来向进程发送信号或实例化一个新线程。
  • UNIX domain socket提供了一个特性允许进程间传递文件描述符。这样一个几次呢很难过就能够打开一个文件并使之对另一个本来无法访问该文件的进程可用。
  • UDP(internet domain datagram)socket 允许一个发送者向多个接收者广播或组播一条消息。

        关于进程同步工具,下面几点是值得注意的。

  • 使用fcntl()加上的记录锁由加锁的进程拥有。内核使用这种所有权属性来检测死锁(两个或多个进程持有的锁会阻塞对方后续的加锁请求的场景)。如果发生了死锁,那么内核会拒绝一个进程的一个加锁请求,因此会哎fcntl()调用中返回一个错误标示出死锁的发生。System V和POSIX信号量并没有所有权属性,因此内核不会为信号量进行死锁检测。
  • 但是用fcntl()获得记录锁的进程终止之后会自动释放该记录锁。System V信号量提供了一个类似的特性,即“撤销”特性,但这个特性仅在部分场景中可靠(47.8).POSIX信号量并没有提供类似的特性。

网络通信

        在图43-1中给出的所有IPC 方法中,只有socket允许进程通过网络来通信。socket一般用于两个域中:一个是UNIX domain,它允许位于同意系统上的进程进行通信;另一个是Internet domain,它允许位于通过TCP/IP网络进行连接的不同主机上的进程进行通信。通常,将一个使用UNIX domain socket 进行通信的程序转换成一个使用Internet domain socket进行通信的程序只需要做出微小的改动,这样只需要对使用 UNIX domain socket的应用程序做较小的改动就可以将他应用于网络场景。

可移植性

         现代UNIX实现支持图43-1中的大部分IPC 工具,但POSIX工具(消息队列、信号量以及共享内存)的普及程度远远不及System V IPC,特别是在较早的UNIX 系统上。(只有版本为2.6.x的Linux 内核系列才提供了一个POSIX消息队列的实现以及对POSIX信号量的完全支持。)因此,从可移植性角度来看,System V IPC要优于 POSIX IPC.

Syetem V IPC 设计问题

        System V IPC 工具被设计成独立于传统的UNIX I/O模型,其结果是其中一些特性,其结果是其中一些特性使得他的一些编程接口的用法更加复杂。相应的POSIX IPC 工具被设计用来解决这些问题,特别是下面几点要注意。

  • System V IPC 工具是无连接的,他们没有提供一个打开的IPCA对象的句柄(类似于文件描述符)的概念,在后面的章节中有时候会打开一个SSystem V IPC对象,但这仅仅是描述进程获取一个引用该对象的句柄的简单方式。内核不会记录进程已经打开了该对象(与其他IPC对象不同)。这意味着内核无法维护当前使用该对象的进程的引用技术,其结果是应用程序需要使用额外的代码来直到何时可以安全的删除一个对象。
  • System V IPC 工具的编程接口与传统的UNIX I/O模型是不一致的(他们使用整数键值和IPC标识符他,而不是路径名和文件描述符),并且这个编程接口也过于复杂了。这一点在System V信号量上表现的特别明显

        相反,内核会为POSIX IPC 对象记录打开的引用数,这样就简化了何时删除对象的决策。此外,POSIX IPC 提供的接口更加简单并且与传统的 UNIX 模型也更加一致。

可访问性

         表43-2 中第二列总结了各种IPC工具的一个重要特性:权限模型控制着那些进程能够访问对象。下面介绍各种模型的细节信息。

  • 对一些IPC工具(如FIFO和socket),对象名位于文件系统中,可访问性是根据相关的文件权限掩码(指定了所有者、组和其他用户的权限)来确定的。虽然System V IPC对象并不位于文件系统中,但每个对象拥有一个相关的权限掩码,其语义与文件的权限掩码类似。
  • 一些IPC工具(管道、匿名内存映)被标记称只允许相关进程访问。这里“相关”只通过fork()相关联的。为了使两个进程能够访问同一个对象,其中一个必须要创建该对象,其中一个必须要创建该对象的一个句柄,这样两个进程就能够共享对象了。
  • POSIX 的未命名信号量的可访问性是通过包含该信号量的共享内存区域的可访问性来确定的
  • 为了给一个文件夹所,进程必须要拥有一个引用该文件的文件描述符(即在实践中他必须要拥有打开文件的权限)。
  • 对Internet domain socket的访问(即连接或发送数据包)没有限制。如果有需要的话,必须要在应用程序中是新访问控制。

 持久性

        术语持久性是指一个IPC工具的声明周期。(表43-2中第三列)持久性由三种。

  • 进程持久性:只要存在一个进程持有进程持久的IPC对象,那么该对象的生命周期就不会终止。如果所有进程都关闭了该对象,那么与该对象的有关的所有内核资源都会被释放,所有未读取的数据会被销毁。管道、FIFO以及socket是进程持久的IPC工具。

FIFO的数据持久性预与其名称的持久性是不同的。FIFO在文件系统中拥有一个抿成,但所有引用FIFO的文件描述符都被关闭之后该名称也是持久的。

  • 内核持久性:只有当显式的删除内核持久的IPC对象或系统关闭时,该对象才会被销毁。这种对象的声明周期与是否由进程打开该对象无关。这意味着一个进程可以创建一个对象,像其中写入数据,然后关闭该对象(或终止)。在后面某个石窟另一个对象可以打开该对象,然后从中读取数据。具备内核持久性的工具包括System V IPC和POSIX IPC。在后面章节中用来描述这些工具的示例程序中将会使用这个属性:对于每种工具都实现一个单独的程序,在程序中下黄健一个独享,然后删除该对象,并执行通信或同步操作。
  • 文件系统持久性:具备文件系统持久性的IPC 对象会在系统重启的时候保持其中的信息,这种对象一直存在至被显式地删除。唯一一种具备文件系统持久性地IPC对象是基于内存映射文件地共享内存。

性能

        在一些场景中,不同IPC工具地性能可能存在显著地差异。但在后面地章节中一般不会对他们地性能进行比较,其原因如下:

  • 在应用程序地整体性能中,IPC工具地性能影响因素可能不是很大,并且确定选择何种IPC工具可能不仅仅需要老驴其性能因素。
  • 各种IPC工具在不同UNIX实现或Linux地不同内核中地性能可能是不同地。
  • 最重要地是,IPC 工具地性能可能会受到使用方式和环境地影响。相关地因素包括每个IPC操作交换地数据单元的大小、IPC工具中唯独数据量可能很大、每个数据单元地交换是否需要进行进程上下文切换、以及系统上地其他负载。

如果IPC性能是至关紧要地,并且不存在应用程序在于目标系统匹配地环境中运行地性能基准,那么最好编写一个抽象软件层来向应用程序隐藏IPC工具地细节,然后在抽象层下使用不同地IPC工具来测试性能。

43.5 总结

        本章概述了进程(以及线程)可用来相互通信和同步动作地各种工具。

        Linux 提供地通信工具包括管道、FIFO、socket、消息队列以及共享内存。Linux 提供地同步工具包括信号量和文件锁。

        在很多情况下在执行一个给定地任务时存在多种技术可以用于通信合同不。本章以多种方式对不同地技术进行了比较,其目标时突出可能对技术选择产生影响地一些差异。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值