操作系统Lesson8 - 同步互斥机制和编程方法
忙等互斥与睡眠唤醒
为了解决多个进程之间的操作不会相互干扰,于是有了这两种操作。
- 忙等互斥:小时间事件适用
- 睡眠与唤醒:长时间事件适用
阻塞:睡眠
忙等:自旋锁
内核中有些适合忙等,因为睡眠需要维护睡眠队列等结构,耗时长,有时不如忙等。
如何解决
禁止多个进程同一时刻对共享资源进行读写
- 共享资源包括:共享内存、共享文件等
进程0-3G独立资源,3~4G共享内核,而文件资源等都是共享的
设立临界区
临界区:将一块共享资源框起来,这段代码就叫临界区。
确保一个或多个进程不会在同一时间对同一共享资源(包括但不限于共享内存、共享文件等)进行读写操作。这要求实现一种称为 互斥(Mutual Exclusion) 的机制,即当一个进程正在使用某共享资源时,其他进程必须被阻止访问该资源。
为了正确高效操作临界区资源,对临界区的锁操作需要遵循以下四个原则:空闲让进、忙则等待、有限等待、让权等待
忙等互斥
为了解决互斥问题,下面列出了多种方法。
屏蔽中断
我直接把其他任务对CPU的打屏蔽了不就行了,就可以独占CPU了。
中断分为两种:
外部IO中断、时钟中断
长期屏蔽中断,CPU无法接收IO消息,也无法被时间片打断,也就无法被其他任务调度,如果此时发生错误,CPU也接收不到中断,会导致死机。
执行方法:程序一进入临界区就屏蔽中断,完成后恢复。
可行性
进程进入临界区由谁决定?
- 如果是用户进程,程序员可决定,那黑客程序怎么办?
- 所以一般不会给用户,内核来做
对单核处理系统上,最简单
由于多核CPU执行屏蔽中断代码,只是针对自己一个CPU,而其它CPU不知道,所以使用TSL、XCHG指令,用来把整条数据线掐断。
所以说屏蔽中断一般很短
锁变量
锁分两种:自旋锁和互斥锁
自旋锁:
- 忙等锁,不放弃CPU,轮询CPU,不断查询。
- 适合小周期
互斥锁:
- 阻塞行为,一旦发现资源不可用,睡眠,放弃CPU,等待被另外的程序 段唤醒
- 适合大周期
如果用int数据类型定义自旋锁:
- 我们由上节课可知,从内存取变量分为三步:取出,操作,写回;
- 如果在判断成功后准备将锁置为1时时间片到了,那么锁还是0,被另一个进程抢到然后进入资源区,两个人进入同一资源区,就会造成严重后果。
所以说用软件基本对此(互斥)无能为力。
于是提出了原子变量:执行期间无法打断,要么不开始,要么做完。
由于早期硬件跟不上,于是还是借助软件,实现了:自旋锁。
提供接口:
spin_lock();
关中断,写入、读取数据
spin_unlock();
开中断
现在:有专门的指令集来(TSL)对此操作,特性:测试设置、比较交换。
而对于之前的接口,软间实现不变,OS根据硬件是否支持硬件中断来决定是否翻译为TSL指令集。
严格轮询法
单变量轮询:
- 用进程号
turn
来规定谁能进入临界区; - 弊端:
- 进程0的非临界任务很长,当进程1完成临界任务后将变量设为进程0的进程号后,很快执行完了非临界的任务,
- 然而变量还是进程0的进程号,进程1没办法访问临界区资源了,违背了空闲让进原则。
Peterson算法
有两种:谦让型、主动自信型
问题代码:两个人互相谦让,造成死锁。
enter
函数中:
- 进程0先抢到时间片执行,将自己状态更新为感兴趣,此时时间片结束;
- 进程1也之开始执行这个代码段,也将自己的状态更新为感兴趣;
- 造成死锁,两人互相谦让。
解决方案
引入turn,等待条件改变:检查对方是否想使用,且谦让轮到他
- 首先将自己的状态改为感兴趣;
- 然后尝试谦让给另一个进程:
- 如果对方进程不感兴趣,我直接就能进入临界区访问资源;
- 如果对方也感兴趣,就会检查谦让的这个
turn
轮到谁了,轮到谁,谁能访问,另一个轮询阻塞。
但是未遵循让权等待原则,因为当初设计的时候也没想到这方面。
主动自信型:谁先举手谁先发言。
// 记录当前“轮到”哪个进程进入临界区
int turn;
// 记录每个进程是否有意愿进入临界区
int interested[N];
// 进程号为0或1的进程进入临界区的函数
void enter_region(int process) {
int other = 1 - process; // 计算另一个进程的编号
// 标记本进程希望进入临界区
interested[process] = TRUE;
turn = process; // 尝试将turn设置为当前进程号
// 等待条件:如果turn仍然指向当前进程且另一个进程仍有意愿进入
while (turn == process && interested[other] == TRUE) {
// 忙等待,直到条件不满足
}
}
// 进程离开临界区的函数
void leave_region(int process) {
// 标记本进程已离开临界区
interested[process] = FALSE;
}
TSL指令
从硬件方面锁定,会将总线锁定,其他所有CPU都无法访问,来解决多核问题.
释放锁无需原子操作
XCHG也是类似的指令,都是将之前的状态读出来并测试。
由于硬件指令提供,一个周期就做完了,能实现多CPU操作
原文地址:https://blog.csdn.net/2301_78981471/article/details/144316402
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!