第6讲 互斥和同步(2)⚓
1. 消息传递⚓
信号量和管程都是设计用来解决访问公共内存的一个或多个 CPU 上的互斥问题的,但是对于分布式系统(每个 CPU 都有自己的私有内存),这些机制将会失效。对于不同机器间的进程通信,必须引入消息传递(message passing)等高级通信机制。
消息传递原语:
它们是系统调用而不是语言成分消息传递。
2. Linux 和 Windows 的互斥与同步⚓
Linux 应用程序采用 POSIX 标准用于进程/线程的同步和互斥,包括互斥锁 mutex,条件变量 cond,读写锁 rwlock,信号量(有名信号量、基于内存的信号量/无名信号量)。
Windows 内核支持三种同步对象:互斥对象 Mutex,信号量对象 Semaphore,事件对象 Event。
3. 进程间高级通信⚓
进程间通信(IPC,Inter-Process Communication):在不同进程之间传递或交换信息。
- 低级通信:进程之间控制信息的交换称为低级通信。一般只传送一个和几个字节的信息,达到控制进程的同步和互斥的作用。例如信号量和信号。
- 高级通信:用户可以直接利用操作系统所提供的一组通信命令,高效地传送大量数据的一种通信方式进程间通信。例如共享内存、消息传递、管道、套接字 socket。
(1) 共享内存
在物理内存中划出一块共享存储区,相互通信的进程可以将共享存储区映射到各自的进程地址空间,通过读或写共享存储区数据实现通信。两个不同进程 A,B 将同一块物理内存被映射到各自的进程地址空间,一个进程可以即时看到对方进程对共享内存中数据的更新。
操作系统一般只提供共享的内存空间,进程之间的读写互斥问题需要程序解决。
Linux 的共享内存机制:系统调用 mmap()
,POSIX 共享内存,System V 共享内存。
优点:效率高,因为进程可以直接读写内存,而不需要任何数据的拷贝。
缺点:进程必须在同一台物理机器上才能使用内存。安全性脆弱。
Note
使用全局变量在同一个进程内部的线程之间实现通信,我们不认为是共享内存方法,因为同一个进程的线程是天然共享一块地址空间的。
(2) 消息传递
在消息传递系统中,进程间的数据交换是以消息(message)为单位。程序员直接利用系统提供的一组通讯命令(原语)来实现通讯,按照实现方法的不同,又可分为直接通信方式(消息缓冲机制)、间接通信方式(信箱通信方式)。
直接通信方式(消息缓冲/消息队列):发送进程直接将消息发送给接收进程,将它挂在接收进程的消息缓冲队列上;接收进程从消息缓冲队列中取得消息。发送和接收进程都必须以显式方式提供目标进程标识符,以表明向谁发送或者从谁那里接收消息。
通信原语:
操作系统在内存中开辟若干个消息缓冲区,用以存放消息,并且负责管理消息缓冲区以及消息的传递。每当一个发送进程向另一个接收进程发送消息时,便申请一个消息缓冲区,并把已经准备好的消息送到缓冲区,然后把这个缓冲区插入到接收进程的消息队列中,最后通知接收进程。接收进程收到发送进程发来的通知后,从本进程的消息队列中摘下消息缓冲区,取出需要的信息,然后把缓冲区还给系统。
Linux 支持两种消息队列:POSIX 消息队列,System V 消息队列。
间接通信方式:利用信箱通讯方式,发送者不必写出接收者,可以向不知名的进程发送消息,由系统选择接收者,因此可以广泛运用于计算机网络系统中。由于消息可以完好的保存在信箱里,只允许核准的目标用户读取,因此可以实现实时通信也可以实现非实时通信。如 WIndows 邮件槽。
(3) 管道 pipe
在进程间以字节流方式传送信息的通信通道。每个管道具有两个端,一端用于输入,一端用于输出。在相互通信的两个进程中,一个进程将信息送入管道的输入端,另一个进程就从管道的输出端读取该信息。由此一个进程的输出可成为另外一个进程的输入,例如常见的 ls | grep xxx
命令。
Note
消息传递通信是以内存缓冲区为基础。管道是以文件系统为基础。
管道的分类:
- 无名管道(anonymous pipe),也可直接代指管道 pipe,可用于具有亲缘关系的进程间的通信。
- 命名管道(named pipe):有名管道克服了管道没有名字的限制,除具有管道所具有的功能外,还允许无亲缘关系的进程间的通信。
在 Linux 中,通过将两个 file 结构指向同一个临时的 VFS 索引节点,而 VFS 索引节点又指向同一个物理页而实现管道。例如:
- 通过 pipe 系统调用创建无名管道,得到两个文件描述符,分别用于写和读。
int pipe(int fildes[2])
,文件描述符fildes[0]
为读端,fildes[1]
为写端。通过系统调用 write 和 read 进行管道的写和读。 - 进程间双向通信,需要两个管道。
- 只适用于父子进程之间或父进程安排的各个子进程之间。