操作系统6-同步互斥

  1. why 同步机制? 因为操作系统需要利用同步机制在并发执行的同时保证一些操作是原子操作
  2. 怎么做到同步问题 ---->临界区
    ------>方法1:禁用硬件中断
    ------>方法2:基于软件的解决方法
    ------>方法3:更高级的抽象方法

临界区 critical section:进程中访问临界资源的一段需要互斥执行的代码

entry section//进入区
   critial section//在这段代码中任何时刻只允许一个代码运行
exit section
   reminder section

要做到任何时刻在临界区中只允许一个代码运行:
1)设置一个进入区entry section,一段代码进入前要先检查一下临界区现在是否可以进入,如果可以进入就设置相应“正在访问临界区”标志
2)进入临界区之后(最后一定会进入的),执行完代码,需要经过一个退出区,清除“正在访问临界区”的标志
3)剩下的就是与临界区无关的区域,我们称为剩余区

  1. 临界区的访问原则:
    ▲空闲则入:没有进程在临界区时,任何进程可进入
    ▲忙则等待:如果临界区现在已经有进程了,其他进程需先等待
    ▲有限等待:等待进入临界区的进程不能无限期等待
    ▲让权等待:不能进入临界区的进程,应该释放CPU,如转换到阻塞状态
  2. 临界区的实现方法:
    ▫ 禁用中断
    ▫ 软件方法
    ▫更高级的抽象方法

方法1,禁用中断:禁止硬件中断的响应

没有中断,就没有上下文切换,就没有并发,将中断处理延迟到中断被启用之后,
进入临界区就禁止所有中断,并保存标志,离开临界区恢复所有中断并恢复标志

  • 局限性很强,因为
    1,禁用中断后,进程无法被切换停止(一直占用CPU在运行),因此整个系统都会为此停下来,也可能导致其他进程处于饥饿状态
    2,临界区可能很长,就是说无法确定响应中断需要的时间 ,(可能存在硬件影响)

方法2,基于软件的同步方法:在两个进程之间通过共享变量的访问来实现同步

两个线程,T0,T1,它们的代码都是

do{
enter section//进入区
  critical section
exit section//退出区
  reminder section
  }while(1)
  • Peterson算法:满足Ti和Tj之间互斥的经典的基于软件的解决方法
    ⇲⇲共享变量
int turn;//表示该谁进入临界区
boolean flag[];//表示进程是否准备好进入临界区,即谁想进入

⇲⇲进入区代码

flag[i] = true;
true = j;
while(flag[j] && turn == j)//j想进入且轮到j了才能进入

⇲⇲退出区代码

flag[i] = false;

⇲⇲ 整合

//线程Ti的代码
do{
flag[i] = true;
turn =j;
while(flag[j] & turn == j)
    critical section
flag [i] = false;
    reminder section
 }while(true);
  • 缺点:比较复杂,需要两个进程间共享数据项,多个进程的话就会更加复杂;并且需要忙等待,浪费CPU时间(感觉不太好,Pertson算法我也看不太进去233,就先……8)

方法3,更高级抽象的同步方法,实际是用硬件的同步原语来实现的同步方法

硬件提供了一些同步原语,如中断禁用,原子操作指令等,OS基于这些同步原语提供更高级的编程抽象来简化进程同步,例如锁,信号量

锁:一个抽象的数据结构

一个二进制变量(锁定/解锁)
Lock::Acquire():锁被释放前一直等待,然后等到锁
Lock::Release():释放锁,唤醒任何等待的进程

  • 使用锁来控制临界区的访问
    lock_next_pid -> Acquire();
    new_pid = next_pid++;
    lock_next_pid->Release()
辅之一些特殊的原子操作指令
1. 测试和置位指令Test-and-Set
  1. 从内存单元中读取值
  2. 测试该值是否为1,然后返回真或假
  3. 内存单元值设置为1
2. 交换指令exchange
  1. 交换内存中的两个值
基于这两个原子指令来实现锁
  1. 使用TS指令实现自旋锁spinlock
class Lock{
int value = 0;
}
Lock::Acquire(){
while (test-and-set(value))
;
}
Lock::Release(){
value = 0;
}

如果锁被释放了,那么TS指令读取0并将值设为1,表明锁被设置为忙且需要等待完成;如果处于忙状态,那么TS指令读取为1不改变锁的状态且需要循环,所以在等待的时候会消耗CPU,即忙等待

  1. 改进1的忙等待锁,改为无忙等待,设置一个等待队列
class Lock{
int value = 0;
WaitQueue q;//加入一个等待队列
}
Lock::Acquire(){
while (test-and-set(value))
{
     add this TCB to this to queue q;//放到等待队列中
     schedule();//调用调度程序函数,使得其他进程可以执行
//;改变忙等待
}
Lock::Release(){
value = 0;//释放锁
remove ont thread t from q;
wakeup(t);
  1. 用交换指令实现锁是同理

oooook,实验部分还是要去做一做印象才能深啊,理论知识还是看一遍看懂过两天又忘记,赶紧复习完走计网吧,效率啊效率,要学的东西要填的坑劈头盖脸呀,明天上午整理一下信号量与管存(重点),死锁与进程通信,文件系统,差不多后天就撒花8888❀❀❀,mysql的进度也要follow上,争取大后天就explain了解起来。三月还有半个月就把重点放在计算机网络(7)和mysql上吧,ucore的实验做做对linux也会有很大帮助的。心态要稳住,主要是效率。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值