进程间通信、互斥、同步

进程通信

概念:进程通信是指进程之间的信息交换,进程是分配系统资源的单位
三种方式:共享存储、管道通信、消息传递

共享存储

两个进程对共享空间的访问必须是互斥的(互斥访问通过操作系统提供的工具实现,如 P、V 操作)
1. 基于数据机构的共享
2. 基于存储区的共享

管道通信
管道通信.png

管道 是指用于连接读写进程的一个共享文件,又名 pipe 文件。其实就是在内存中开辟一个固定大小的缓冲区。
1. 管道只能采用半双工通信
2. 各个进程要互斥的访问管道
3. 数据以字符流的形式在管道中传输,管道写满时写进程的 write() 被阻塞,直到数据被读进程全部取走,之后读进程的 read() 被阻塞
4. 如果没写满就不允许读,如果没读完就不允许写
5. 读进程最多只有一个

消息传递
消息传递.png

进程间的数据交换以 格式化信息 为单位,进程通过操作系统提供的“发送消息/接收消息”两个原语进行数据交换
消息传递分为:直接通信方式(通过消息缓冲队列)、间接通信方式(通过信箱)


进程间互斥与同步

临界区(Critical Section)同步问题:
不同进程直接共享的数据,在并发运行时会产生临界区问题:确保当一个进程 i 正在其临界区执行时,没有其他任何进程也在 i 进程的临界区执行。

临界区同步问题解决方案的必要条件:
a. 互斥条件:只能有有限个进程在临界区里面
b. 空闲让进:如果 没有进程处于它的临界区 而且 有进程申请进入其临界区,那么只有那些不在其他代码段(Remainder Section)的进程才能参与到临界区的选举 而且 这个选举不允许无限期推迟。
c. 有限等待:某个进程从提出申请到获准进入临界区之间其他进程进入临界区的次数是有限的
d. 让权等待:对于等待进入临界区的进程而言,它必须立即释放处理机,以免进程“忙等”

临界区同步算法:

软件算法:

Peterson 算法(两个进程)

// P0进程
flag[0] = true;  // 表示想进入临界区的意愿
turn = 1;          // 表示愿意等待其他进程先进入临界区
while (flag[1] && turn == 1);  // 表示只有在其他进程不需要进入临界区时或者其他进程愿意等待时,P0才进入临界区
critical section;
flag[0] = false;
remainder section;
// P1进程
flag[1] = true;
turn = 0;
while (flag[0] && turn == 0);
critical section;
flag[1] = false;
remainder section;

Lamport 面包房算法(N 个进程)
pass

硬件算法:

中断屏蔽算法(不适用于多处理器)
TestAndSet 算法(又称 TSL 指令)
Swap 指令

信号量机制

信号量是个变量,除了初始化外,只能做两个操作(P、V):wait() 和 signal(),这两个操作都是原语操作,因此都不会被中断
P、V 操作必须成对出现
1. 整形信号量(会导致忙等,不符合让权等待原则)

// 初始化信号量,信号量表示资源个数
int S = 1;
// 进入区,判断资源是否足够进入临界区
void wait (S) {
  while (S <= 0);
  S = S - 1;
}
// 退出区,释放资源
void signal (S) {
  S = S + 1;
}

2. 记录型信号量

/* 记录信号量的定义 */
typedef struct {
  int value;
  Struct process *L;
} semaphore;

void wait (semaphore S) {
  S.value--;
  if (S.value < 0){
    block(S.L)
  }
}

void signal (semaphore S) {
  S.value++;
  if (S.value <= 0){
    wakeup(S.L)
  }
}
信号量机制实现进程同步

a. 设置同步信号量 S,初始为 0
b. 在“前操作”之后执行 V 操作
c. 在“后操作”之前执行 P 操作

int S = 0;
P1 () {
  前操作。。。
  V(S)
}
P2 () {
  P(S)
  后操作。。。
}

管程

管程是一种特殊的软件模块(类似于类),由以下部分组成
1. 共享数据结构
2. 操作共享数据结构的一组过程(函数)
3. 共享数据初始化语句
4. 管程有一个名字
管程的特征:
1. 局部于管程的数据只能被局部于管程的过程所访问
2. 进程只能通过管程中的方法对管程中的共享数据进行访问
3. 每次仅允许一个进程对管程进行访问
为什么要使用管程?
解决信号量机制编程麻烦、易出错的问题

推荐阅读更多精彩内容