
大家好,很高兴又和大家见面啦!!!
在前面的内容中,我们一同探讨了进程的“内心世界”:从进程作为程序执行实体的基本概念,到其动态变化的生命状态,以及操作系统如何通过进程控制(如创建、切换、终止)来精准地调度这些“任务单元”。我们看到了每个进程都拥有独立的内存空间,像一个戒备森严的私人办公室,这保证了系统的稳定与安全。
然而,一个显而易见的问题随之产生:如果所有进程都如此“与世隔绝”,我们电脑上那些需要默契配合的应用程序(比如一边听音乐一边写作)又将如何协同工作?这些独立的“办公室”之间,如何才能安全、高效地传递信息、协调步伐?
这正是我们接下来要揭晓的答案。进程的独立性恰恰构成了它们之间需要通信的根本原因。本文将深入讲解操作系统是如何搭建起一座座跨越隔离的桥梁,实现进程间通信(IPC) 的。现在,就让我们一同走进这座连接进程孤岛的精密工程。
进程通信(Inter-Process Communication, IPC)是指运行在不同进程之间的信息交换机制。
之所以不同进程之间的通信需要特定的交换机制,是因为进程的独立性。
操作系统在创建每一个进程时,都会为其分配一块独立的内存空间,并且其它的进步无法直接访问该空间。因此进程与进程之间想要进行通信,就需要通过特定的信息交换机制来实现。
根据交换信息量的多少和复杂度,进程通信可分为两类:
接下来,我们将进一步深入探讨这三种经典模型;
共享存储 指的是在通信的进程之间存在一块 可直接访问的共享空间,通过对这片共享空间进行读/写操作实现进程之间的信息交换。
flowchart LR
subgraph A[内存空间]
direction LR
a[进程1]
b[进程2]
c[共享空间]
a--->c--->a
b--->c--->b
end进程在对共享空间进行读/写操作时,需要使用同步互斥工具对共享空间的读/写进行控制。如 P操作 和 V操作。
共享存储分为两种:
我们可以将这两种共享方式简单的理解为:
进程通过共享存储方式进行通信时,其核心在于由通信进程直接通过共享的存储区域进行数据交换,而数据交换的逻辑、时机与语义则由进程自身控制和解释
消息传递指的是进程之间以格式化的消息(Message) 为单位,通过操作系统提供的发送消息/接收消息这两个原语 进行数据交换。
flowchart LR
a[进程1]--->b[进程2]--->a这种通信方式隐藏了通信实现细节,使通信过程对用户透明,简化了通信程序的设计,是当前应用最广泛的进程间通信机制。
在微内核操作系统中,微内核与服务器之间的通信就采用了消息传递机制。该机制能很好地支持多 CPU 系统、分布式系统和计算机网络,因此也成为这些领域最主要的通信工具。
消息传递这种通信方式按照传递方式的不同,可以分为两类:
简单的理解就是:
flowchart LR
subgraph a[进程1]
a1[消息队列]
end
subgraph b[进程2]
b1[消息队列]
end
a--->|格式化消息1|b1
b--->|格式化消息2|a1flowchart LR
subgraph a[进程1]
end
subgraph b[进程2]
end
subgraph c[信箱]
end
a--->|格式化消息1|c--->|格式化消息1|b
b--->|格式化消息2|c--->|格式化消息2|a这两种通信方式从图示中就可以看到区别:
这就好比小红与小明这两个朋友平时之间的通信方式都是通过书信的形式:
管道 是一个特殊的共享文件,也称为 pipe 文件,数据在管道中遵循 先进先出 (First _ In _ First _ Out, FIFO)的原则。
管道通信 是指两个进程之间通过 管道 完成数据交换。该通信方式允许两个进程按照 生产者-消费者 的方式进行通信。
flowchart LR
a[进程1]
subgraph b[管道]
b1[消息1]
b2[消息2]
b3[...]
end
c[进程2]
a--->|发送消息|b--->|接收消息|c在同一时间内,单个 管道 中只能够实现单向的通信,即管道通信只能实现半双工通信:
若同一时间内,进程1要向进程2发送信息,进程2也要向进程1发送信息,这时就需要两条互斥的管道完成:
flowchart LR
subgraph a[进程1]
direction LR
a1[发送信道]
a2[接收信道]
end
subgraph B[管道]
direction LR
subgraph b[管道1]
b1[消息1]
b2[消息2]
b3[...]
end
subgraph d[管道2]
d1[消息1]
d2[消息2]
d3[...]
end
end
subgraph c[进程2]
direction LR
c2[接收信道]
c1[发送信道]
end
a--->B--->c
c--->B--->a在管道通信中,为了协调双方的通信,管道机制必须满足三方面的协调能力:
通信双方如果是以 管道通信 的方式实现的信息交换,那么必然满足下面的条件:
Linux 中的管道通信在 Linux 中,管道是一种使用十分频繁的通信机制。从本质上说,管道也是一种文件,但它又和一般的文件有所不同,管道可以克服使用文件进行通信的两个问题:
Linux 中,管道的大小为 4KB ,这是的它的大小不像普通文件那样不加检验的增长。write() 调用将会默认被阻塞,等待某些数据被读取,以便腾出足够的空间供 write() 调用read() 调用会被阻塞,等到某些数据的写入标准的匿名管道只允许存在一个读进程与一个写进程。
不过对于满足 FIFO 这种特性的队列而言,它是能够天然的支持 多个写进程 ,这是因为写入的数据会按写入的顺序进行排列,并不会对管道造成什么影响。
但是当多个读进程来同时读取该管道时,就可能出现数据争抢的情况,多个读者读取同一份数据,这就会导致读取失败的问题。
这是因为管道中的数据被读取后,就会从管道中消失,为新的数据腾出空间,以便进行新的写入操作,即一个数据只能被一个读者获取,在这种情况下,其它读者就可能出现以下情况:
所以,我们可以得到结论:
但是在实际的使用中,同一个管道是允许 多个写进程 写入数据,同时也允许 多个读进程 来读取数据。只不过在这种情况下,多个读进程读取同一条管道中的数据时,需要经过一些特殊处理,才能够正常的读取数据:
不管是上述的哪种特殊处理,都能够实现管道的多写多读。
管道只能由创建创建进程所访问。
想要成功的创建一个管道,就必须保证:
因此当一个父进程创建了一个管道后,子进程会继承父进程的管道,并可以利用该管道来与父进程进行通信。
今天的内容到这里就全部结束了。通过今天的学习,我们系统地揭开了进程间通信(IPC)的神秘面纱。进程的独立性是通信机制存在的根本原因,而操作系统则为我们提供了多种精巧的“桥梁”来跨越这一鸿沟。
我们深入探讨了三种经典的高级通信模型:
回顾全文,这三种机制各具特色,但它们共同解决了同一个核心问题:如何在保证进程独立性的前提下,安全、高效地实现数据交换与进程协同。
理解这些基础通信模型,不仅是掌握操作系统原理的关键一环,也为今后学习更复杂的分布式系统、网络编程等知识奠定了坚实的基石。
希望本篇内容能帮助您清晰地构建起进程通信的知识框架。