相关文章
容器技术回顾 - 使用 UDS 实现 Pod 间通信
容器技术回顾 - 多 Pod 间共享内存通信
------正文开始------
正在运行的 Linux 系统由许多进程组成,其中许多进程彼此独立运行。但是,有些进程需要协作才能实现其预期目的,这些进程需要相互通信和同步其操作的方法。
进程通信的一种方式是通过读写磁盘文件中的信息。然而,对于需要实时通信或需要在不同主机上运行的进程之间共享数据的应用程序来说,这种方式通常太慢且不够灵活。
为了解决这些限制,Linux 提供了一套丰富的进程间通信(IPC)机制,包括:
信号:信号是进程之间相互发送通知的一种轻量级方式。信号可用于指示已发生某个事件,例如收到新消息或终止另一个进程。
管道和 FIFO:管道和 FIFO 是可用于在进程之间传输数据的单向通道。管道由内核创建,而 FIFO 由用户空间进程创建。
套接字:套接字是双向通道,可用于在同一主机或通过网络连接的不同主机上的进程之间传输数据。套接字是一种比管道或 FIFO 更通用的 IPC 机制,通常用于需要可靠和高性能通信的应用程序。
文件锁:文件锁定允许进程锁定文件的区域,以防止其他进程读取或更新文件内容。文件锁定通常用于实现共享文件的进程之间的同步。
消息队列:消息队列是进程之间交换消息(数据包)的一种方式。消息队列是一种比管道或 FIFO 更灵活的 IPC 机制,可用于实现各种通信模式。
信号量:信号量是进程同步其操作的一种方式。信号量可用于实现互斥、计数信号量和屏障。
共享内存:共享内存是两个或多个进程共享一块内存的方式。当一个进程更改共享内存的内容时,所有其他进程都可以立即看到这些更改。共享内存是一种非常高效的 IPC 机制,但正确使用起来可能有些困难。
Linux 系统上可用的 IPC 机制种类繁多,这反映了不同应用程序的不同需求。例如,管道和 FIFO 是需要快速传输少量数据的应用程序的不错选择,而套接字则是需要通过网络可靠地传输大量数据的应用程序的更好选择。
IPC 机制的选择还取决于应用程序的具体需求。例如,如果应用程序需要实现互斥,那么信号量就是最佳选择。如果应用程序需要与其他进程交换消息,那么消息队列就是最佳选择。
一般来说,选择 IPC 机制的最佳方法是考虑应用程序的具体要求,并选择最能满足这些要求的机制。
互斥锁
临界区问题的硬件解决方案非常复杂,而且应用程序员通常无法理解。相反,操作系统设计人员构建了更高级的软件工具来解决临界区问题。这些工具中最简单的就是互斥锁。(事实上,术语“互斥”是“mutual exclusion”的缩写。)我们使用互斥锁来保护临界区,从而防止出现竞争条件。也就是说,进程必须在进入临界区之前获取锁;它在退出临界区时释放锁。acquire() 函数获取锁,release() 函数释放锁。
互斥锁具有一个布尔变量,其值指示锁是否可用。如果锁可用,则对 acquire() 的调用会成功,然后锁将被视为不可用。尝试获取不可用锁的进程将被阻止,直到锁被释放。
信号量
正如我们前面提到的,互斥锁通常被认为是最简单的同步工具。在本节中,我们将介绍一种更强大的工具,它的行为类似于互斥锁,但也可以为进程提供更复杂的同步活动方式。
信号量 S 是一个整型变量,除了初始化之外,它只能通过两个标准原子操作访问:wait() 和 signal()。信号量是由荷兰计算机科学家 Edsger Dijkstra (https://en.wikipedia.org/wiki/Edsger_W._Dijkstra)引入的,因此 wait() 操作最初被称为 P(来自荷兰语 Proberen, “测试”);signal() 最初被称为 V(来自verhogen, “增加”)。wait() 的定义如下:
wait(S) { while (S <= 0) ; // busy wait S--;}
signal()的定义如下:
signal(S) { S++;}
wait() 和 signal() 操作中对信号量整数值的所有修改都必须以原子方式执行。也就是说,当一个进程修改信号量值时,其他任何进程都不能同时修改同一个信号量值。此外,在 wait(S) 的情况下,对 S 的整数值 (S ≤ 0) 的测试及其可能的修改 (S — ) 都必须不间断地执行。
信号量实现
互斥锁的实现存在忙等待问题。刚刚描述的 wait() 和 signal() 信号量操作的定义存在同样的问题。为了解决这个问题,我们可以修改 wait() 和 signal() 操作的定义,如下所示:当进程执行 wait() 操作并发现信号量值不为正时,它必须等待。但是,进程可以自行暂停,而不是忙于等待。暂停操作将进程放入与信号量关联的等待队列中,并将进程的状态切换为等待状态。然后,控制权将转移到 CPU 调度程序,后者选择另一个进程来执行。
预告
下周我们会重点聊一聊进程间通信,参考自《Linux 网络编程》。
1 进程间通信的一些基本概念2 信号2.1 信号的处理2.2 信号与系统调用的关系2.3 信号的复位2.4 在进程间发送信号2.5 系统调用alarm()和pause()2.6 系统调用setjmp()和longjmp()3 管道3.1 用C 来建立、使用管道3.2 需要注意的问题4 有名管道4.1 有名管道的创建4.2 有名管道的I/O 使用4.3 未提到的关于有名管道的一些注意4.5 文件和记录锁定5.1 实例程序及其说明5.2 锁定中的几个概念5.3 System V 的咨询锁定5.4 BSD 的咨询式锁定5.5 前面两种锁定方式的比较5.6 Linux 的其它上锁技术6 System V IPC 6.1 ipcs 命令6.2 ipcrm 命令7 消息队列(Message Queues)7.1 有关的数据结构7.2 有关的函数7.3 消息队列实例——msgtool,一个交互式的消息队列使用工具8 信号量(Semaphores)8.1 有关的数据结构8.2 有关的函数8.3 信号量的实例——semtool,交互式的信号量使用工具9 共享内存(Shared Memory)9.1 有关的数据结构9.2 有关的函数9.3 共享内存应用举例——shmtool,交互式的共享内存使用工具9.4 共享内存与信号量的结合使用