目录
0.前言
本系列文章旨在记录操作系统的知识点,可用于期末复习,笔者理解尚浅,文中不正之处静待批正。加粗高亮部分为重点。
1.并发(Concurrency)的原则
并发的出现场景:多应用程序、结构化应用程序、操作系统结构
并发的本质:进程间的资源争夺
现象:死锁、饥饿、结果不确定
解决方法:互斥(Mutual Exclusion),设立临界区(critical section)一次只允许一个代码执行
1.1 几个重要概念
- 临界区(critical section):进程中需要访问共享资源的一段代码,并且当另一个进程位于相应的代码段时,该代码可能不会执行
- 死锁(deadlock):是由于每个进程都在等待另一个进程执行某项操作,导致两个或多个进程无法继续进行的一种情况
- 活锁(livelock):两个或多个进程连续地改变它们的状态以响应其他进程的变化,而不做任何有用的工作
- 互斥(mutual exclusion):当一个进程处于访问共享资源的临界区时,其他进程不能处于此临界区
- 竞争条件(race condition):多个线程或进程读写共享资源,最终结果取决于它们执行的相对时间
- 饥饿(starvation):一种可运行进程被调度程序无限期忽略的情况,虽然它可以继续,但它永远不会被选择
1.2 互斥的条件
- 一次只允许一个进程进入临界区,忙则等待
- 阻塞于临界区外的进程不能干扰其他进程
- 不能发生死锁与饥饿,有限等待
- 闲则让进
- 对相关进程的执行速度和CPU数目无要求
- 有限占用
2.互斥的实现
2.1 硬件方法(无中断)
①中断屏蔽(Interrupt Disabling)
while(true){
/*disable interrupts*/ //关中断,不能发生进程切换
/*critical section*/
/*enable interrupts*/ //开中断
/*remainder*/
}
缺点:只能用于单处理器,只能用于内核进程
②特殊的机器指令
1) bolt 插销(全局变量)
boolean testset (int &bolt) {
if (bolt == 0) {
bolt = 1;
return true;
}
else {
return false;
}
}
2) compare and swap(CAS)思想
int compare_and_swap(int *bolt, int testval, int newval)
{
int oldval;
oldval = *bolt;
if ( oldval == testval) *bolt= newval;
return oldval;
}
3) exchange指令
void exchange(int register, int memory) {
int temp;
temp = memory;
memory = register;
register = temp;
}
优点:无关进程数和处理器数;简单易校验;支持多临界区
缺点:忙等消耗时间;没有解决饥饿与死锁
2.2 软件方法
①单标志法
分析:与CAS思想刚好相反,CAS是进程间抢夺进入临界区的权限,而单标志法是轮流让进程进入临界区。由于turn表示允许进入临界区的进程号,因此必须要求两个进程交替进行,如果一个进程想连续执行两次就会发生死锁。所有我们可以想到为每个进程设立自己的标志flag。
②双标志先检查法
分析:第①个方法的turn表示是不是自己的轮次,而这里的flag表示当前进程的意愿,因此当别人不想进的时候,就将自己的flag设为true,但是这种操作不是原子指令,因此有可能两个线程都进入了临界区。
③双标志后检查法
分析:先将自己的意愿设为true,再判断别人的意愿,因为不是原子操作,同样可能发生死锁现象。考虑将turn和flag结合起来。
④Peterson算法
分析:如果对方想进并且也是对方的轮次,则自己等待。但这种方法不允许同一个进程多次执行,只能交叉执行。
⑤Dekker算法
分析:牢记turn表示是不是自己的轮次,flag表示自己的意愿。先将自己的意愿设为true,然后循环判断对方的意愿,如果都想进入临界区,则通过对turn的状态判断决定谁进入临界区,谁让权等待。让权等待的进程将自己的意愿设为false,并等待对方执行完毕后再设为true。
3.信号量(Semphores)
3.1 信号量的定义和实现
信号量Semaphore : sem变量 + 2 atomic operations原子操作
①semWait(P):抢信号;--sem;若sem<0,则进程被阻塞
②semSignal(V):发信号;++sem;若sem<=0,则被semWait阻塞的进程被唤醒
Semaphore Primitives(原语)的定义:
3.2 信号量的应用
3.2.1 互斥
当只有两个线程竞争同一资源,信号量初始值必须为1
若信号量只能为0,1,则使用二元信号量(Binary Semaphore)
二元信号量的原语:
当有k(k>2)个线程竞争时,使用计数信号量(counting semaphore),初始值也为1,此时信号量可以为0(有线程正在使用),-m(表示有m个线程正在等待),1(空闲);
信号量用于互斥的框架:
信号量与互斥锁(MutexLock)的区别:
①互斥锁只能在相同线程上锁和开锁
②信号量可以在不同线程上锁和开锁
关于信号量初始值的讨论sem=n:
- n=0,同步问题(生产者消费者问题)
- n=1,互斥问题(-m表示m个线程在等待)
- 二元信号量,2个线程互斥
- 计数信号量,K(k>2)个线程互斥
- n>1, 互斥(资源数为n,生产者消费者、哲学家就餐等)
强信号量:等待的线程遵循FIFO
弱信号量:未定义移出顺序
3.2.2 同步synchronize
生产者消费者问题:
①当buffer为1
分析:将PV操作理解为接收和发送信号的过程。对于生产者,等待盘子为空的信号,然后进行生成,最后放出盘子已满的信号;对于消费者,等待盘子已满的信号,然后进行消费,最后放出盘子已空的信号。
②当buffer为有限个
分析:当缓冲区>1时,此时需要对每一个盘子维护一个信号量,同时初始化时空盘子数即为缓冲区大小,并且一个盘子只能被一个生产者持有,所以必须要引入互斥锁,使只有持有锁的进程才能被执行。
③当buffer为无限个
分析:由于有无数个空盘子,则不再需要接收盘子为空的信号,因此肯定会有空盘子。同时依然需要互斥锁来保证临界区只有一个进程。
④循环buffer
分析:需要注意当buffer为循坏时while中的判断语句。
当信号量初始值为0表示同步(one after another),当初始值为1表示竞争
当缓冲区>1时,可以交换semSignal的位置,但不能交换semWait的位置
同步与互斥题型总结——操作系统(Operating System)专题篇——同步与互斥1-CSDN博客
4.管程(Monitor)
组成:管程由一个或者多个例程、一段初始化代码和局部数据组成
特征:
- 进程调用管程函数即进入管程
- 局部变量只能被管程使用
- 一次只能进入一个进程
管程通过条件变量(condition variable)实现同步:cwait,csignal
管程与信号量的区别:管程的signal信号若没有任务在等待,则会丢失
4.1 几个重要概念
- 信号量:一个进程间通信的整数。信号量只能执行三种操作,它们都是原子操作:初始化、递减和递增。递减操作可能导致进程阻塞,而递增操作可能解除进程阻塞。也称为计数信号量或通用信号量。
- 条件变量:一种数据类型,用于阻塞进程或线程,直到特定条件为真为止。
- 管程:一种将变量、访问过程和初始化代码封装在抽象数据类型中的编程语言结构。管程的变量只能通过访问其例程进行访问,并且一次只能有一个例程处于活动状态。访问例程即临界区。管程可能有一个等待访问它的进程队列
总结:
5.消息传递(Message Passing)
发送发send,接收方receive;都可以处于阻塞态或非阻塞态
blocking send表示邮箱已满,blocking receive表示没有自己的消息
①Blocking send, blocking receive
当消息队列满时,send将会阻塞,直到消息能写进消息队列;当未收到消息时,receive会阻塞,直到有消息到达
②Nonblocking send, blocking receive
send可能一直发,但若无消息到达,,则receive会一直阻塞、
③Nonblocking send, nonblocking receive
任何部分均不会阻塞
寻址模式:
直接寻址Direct addressing:发送放必须指定目的进程的标识(直接发给接收方)
间接寻址Indirect addressing:均放入邮箱(消息队列),并从邮箱收取
消息的格式:Header+Body
6.读写者问题
见同步与互斥专题篇——操作系统(Operating System)专题篇——同步与互斥1-CSDN博客