操作系统课堂笔记五-同步互斥机制

同步互斥机制

进程互斥

  • 定义: 当很多进程需要共享资源时, 而这些资源又具有排他性,那么各个进程竞争使用这些资源的关系称为进程互斥

临界区

  • 临界资源(critical source): 又称作互斥资源, 特点是一次只能给一个进程使用, 当多个进程都需要这些资源时候, 就衍生出了临界区或者说互斥区
  • 临界区(critial section): 各个进程中对某个临界资源(共享变量)实施操作的程序片段
  • 临界区使用原则
    • 临界区没有进程时候, 进程可以随时进入
    • 临界区只能有一个进程存在
    • 临界区外进程不能阻塞程序进入临界区
    • 不能使进程无限等待进入临界区

实现进程互斥方案

  • 软件方案
    • DEKKER算法(伪代码), 假设只有p, q两个进程, 代码给出了其中一个,另一个一样的思路
      // p进程
      pTurn = true;
      while(qTurn){
          if (turn == 2){
          	pTurn = false;
          	while (turn==2);
          	pTurn = true;
          }
      }
      // 这里是临界区
      intoSection()
      // 释放
      turn = 1;
      pTurn = false;
      // 缺点: 浪费CPU等待时间
      // 优点: 确实解决了临界区的问题
      
    • PETERSON算法(伪代码)
      void enter_region(int processId){
      	int otherId;
      	otherId = 1 - processId;
      	// 表示感兴趣进入临界区
      	interest[processId] = true;
      	turn = processId;
      	// 核心是这一步(保证了先到先进入临界区)
      	while(turn==processId && interest[otherId]==true);
      }
      void leave_region(int processId){
      	interest[processId] = false;
      }
      

      ps.感觉这种算法依旧会浪费CPU

  • 硬件方案
    • 中断屏蔽方法(伪代码)
    关中断
    ->临界区
    开中断
    
    • 优点: 方便 简洁 适合系统进程
    • 缺点: 代价大, 不适合多处理器, 不适合用户进程
    • TSC指令
      • 一条指令做了两件事: 读寄存器中的值判断临界区有无内容 ,有则继续读, 没则进入并且加锁
    • 交换指令

进程同步

  • 指多个进程中发生的事件存在某种时序关系, 说白了就是某一个进程需要继续往下走. 需要另一个进程的帮助, 否则就进入阻塞状态
  • 生产者/消费者问题

信号量(PV操作)

  • 信号量: 用于进程之间传递信息的整数
  • 相关结构体
struct semaphore{
	int count;
	queueType queue;
}
  • p操作(伪代码演示)
P(s){
	s.count --;
	if (s.count < 0){
		// 该进程进入阻塞状态, 下CPU进入等待队列队尾
	}
	// 否则继续执行
}
  • v操作(伪代码演示)
V(s){
	s.count ++;
	if (s.count <= 0){
		// 说明有任务在阻塞队列中, 阻塞任务插入就绪队列中
	}
}
  • 说明: p,v操作都是原子操作

pv操作解决互斥问题

  • 初始化信号量mutex=1
  • 进入临界区之前P(mutex)
  • 出临界区V(mutex)
  • 案例
    • 假设有三个进程A,B.C
    • A进入临界区, mutex=0
    • B再进去,mutex=-1进不去,送到等到队列
    • C进去 mutex=-2进不去, 送到等待队列
    • A出来 mutex=-1 <= 0 V操作唤醒B进程
    • B进入临界区 再出来 mutex=0 <= 0唤醒C进程
    • 最后C进程进入临界区, 最终mutex=1回到初始状态

信号量解决互斥问题

  • 还是针对生产消费者的问题
  • 假设互斥信号量mutex=1
  • 缓冲区empty个数: N
  • 缓冲区full个数: 0
  • 看一下生产者和消费者伪代码
// 生产者
void producer(){
	while(True){
		item = get_product();
		P(empty);
		// 因为要进入临界区
		P(mutex);
		insert_item(item);
		V(mutex);
		V(full);
	}
}

// 消费者
void consumer(){
	while(True){
		// 这里的full和mutex顺序不能颠倒 会导致一直等待
		P(full);
		P(mutex);
		item = get_product();
		// 消费
		consume_item();
		V(mutex);
		V(empty);
	}
}

读者写者问题

  • 多读者 一写者 读者优先
  • 场景: 多个进程共享一个数据区
    • 读者进程: 在数据区里面读数据
    • 写者进程: 在数据区里面写数据
  • 条件
    • 允许多个读者读
    • 不允许多个写者
    • 不允许读写同时操作
  • 伪代码
// 读者进程
void reader(){
	// 由于多个读者, readerCount也应该当做临界区保护起来
	P(mutex);
	if(readerCount==0) P(w);
	readerCount ++;
	V(mutex);
	// 进去读资源
	read();
	P(mutex);
	readerCount --;
	if (readerCount == 0) V(w);
	V(mutex);
}

void writer(){
	P(w);
	// 写数据
	write();
	V(w);
}
// 写者进程

管程

  • 概念: 管程的出现是因为信号量编程繁琐且容易出错, 比如PV操作顺序可能导致死锁问题. 管程就是一组过程, 这组过程主要是对一些共享资源数据结构的管理。
  • 管程大致的样子(伪代码)
monitor func
	integer i;
	condition c;
	procedure insert();// 生产过程
	...
	...
	end;
	procedure remove(); // 消费过程
	...
	...
	end;
end; //整个过程结束
  • 进程与管程的关系: 进程通过调用管程中的过程, 来间接的获取数据。例如: 生产者需要调用insert()过程, 消费者调用remove()过程

  • 特性

    • 互斥: 使得管程中数据结构完整, 由编译器负责
    • 同步: 在管程中设置了条件变量及等待/唤醒操作以解决同步问题, 也是通过忙等待或者唤醒来操作
  • 管程中会不会出现两个活跃进程?

    • 有可能, 假设A进程进入管程后需要等待资源才能进入下一步, 那么就会在管程内忙等待,并且释放信号量。这时候B进程进入将A唤醒, 这个时候管程里面就有两个活跃进程了。
  • 管程中多个进程解决方案

    • A等待B执行(MESA: 被唤醒的继续等)
    • B等待A执行(Hoare: 被唤醒的先执行)
    • 规定唤醒操作为管程中最后一条语句(pascal)

进程间通信

  • 上面讲述的管程在多处理器的情况下不适合, 因此有了新的通信机制: 消息传递(send/receive原语)
  • 适用场景
    • 分布式系统
    • 基于共享内存的多处理机系统
    • 单处理机系统
  • 消息传递过程
    • send: 该方法调用时会先陷入内核然后将消息复制进入缓冲区(操作系统会找到一个空缓冲区),操作系统会将消息挂接到接收进程的PCB中(可以认为是指向了PCB),然后去做别的事。其中消息包括消息头(消息类型, 接收进程ID和发送进程ID, 消息长度,控制信息等)和消息体(消息内容)
    • receive: 等接收进程上CPU的时候, 执行receive方法, 会陷入内核,操作系统会将缓冲区里面信息复制到相应进程的地址空间
  • 共享内存
    • 两个进程共享一块物理内存, 类似读者写者问题. 每个进程将自己的一块地址空间映射到一块物理内存, 实现通信
  • 管道通信
    • 利用一个传输介质连接两个相互通信的进程

典型操作系统IPC机制

Linux进程间通信机制

  • 内核外同步机制: 管道, 消息队列, 共享内存, 信号量, 信号, 套接字
  • 内核同步机制: 原子操作, 自旋锁, 读写锁, 信号量, 屏障等
原子操作
  • 特点
    • 不可分割,未执行完之前不会被其他事件中断
    • 常用于实现资源的计数
屏障
  • 作用: 对一组线程进行协调,个人觉得就是适用于大规模数据场景

参考

[1] 操作系统原理

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值