2021.11.6
CPU调度策略
在系统调度的Schedule函数中,如果就绪队列中有多个进程,那么CPU应该如何选择?
CPU调度的目标
让进程满意
- 尽快结束任务
周转时间短(从任务进入到任务结束),不能让进程长时间处于等待 - 用户操作尽快响应
响应时间短(从操作发生到响应),如在用户打开一个进程时需要很快展示在系统显示中 - 系统内耗时间少
吞吐量(完成的任务量),即进程切换等耗时
总原则:系统专注于任务执行,又能合理调配任务
CPU调度的矛盾
- 吞吐量和响应时间之间有矛盾
响应时间少,切换次数多,系统内耗大,吞吐量小 - 前台任务和后台任务的关注点不同
前台任务关注响应时间(需要用户尽快看到),后台任务关注周转时间 - IO约束型任务和CPU约束型任务有各自的特点
IO约束型任务有着高的IO频率,IO约束型任务需要给与更高的优先级,因为IO约束型任务,需要频繁切换到IO设备,因此CPU会存在多段的空闲,此时将CPU约束型任务调度即可
总结:操作系统的调度需要折中和综合考虑
CPU调度算法
FCFS
First Come, First Served:先来先服务
将优先到达的进程优先执行,平均周转时间较长
SJF
Short Job First:短作业优先
所需时间较少的进程优先调度,折中方法的平均周转时间是最短的
这样的做法导致长作业会长时间处于饥饿状态,因此对于长作业非常不利
RR
Round-Robin:时间片轮转算法
按时间片来轮转调度,每次每个进程都执行确定的时间,在时间片执行完后将当前进程置于就绪队列末尾,并执行队列中下一个进程
时间片大:响应时间太长;时间片小:吞吐量小
出现的问题
前台任务和后台任务同时出现时,应该如何调度进程
前台任务由于需要及时响应,因此采用RR
后台任务更关心周转时间,因此才用SJF,并且为保证前台任务的响应,只有在前台没有任务时才调度后台任务
这样调度的问题会导致一些后台任务可能永远无法执行
因此考虑将后台任务优先级动态升高,但后台任务一旦执行,前台的响应时间可能会变慢,从而无法满足用户请求
于是再考虑前后台都使用时间片,都又会退化到RR。
另外存在的问题还有如何知道任务是前台任务还是后台任务
gcc、word等也是包含了前台和后台多种操作的任务
Schedule函数
在Linux 0.11中采用Schedule函数进行调度,在该调度方法中,在每个进程中都设定一个counter值,每次调度counter最大的进程,另外counter还作为时间片来进行轮转调度。
对于时间片用完以及非就绪态的进程,将进程的counter/2再加上counter的初值,在这样的情况下,非就绪态的进程的counter会越来越大,并且在进入就绪态时有着很大的counter值,即优先级
对于处于非就绪态的进程,大多是由于IO调用才进入阻塞,IO时间越长,这些进程的counter值就会越来越大,就实现了IO进程的响应时间,即照顾了前台进程。
另一方面,后台进程一直按照counter进行轮转,由于不断地时间片轮转,则短作业会很快会完成,留下长作业,近似了SJF调度
最后,每个进程只需要维护一个counter变量,这个做法能够简单高效地完成调度。
进程同步与信号量
进程合作
多个进程共同完成一个任务
进程可能需要等待其他进程完成后才能继续进行
生产者-消费者实例
通过缓存区状态来判断生产者和消费者进程需要的状态
信号
在遇到阻塞时,进程进入睡眠状态,而在另一个进程完成生产/消费之后,该进程唤醒本进程。
信号处理存在的问题
单用信号和唤醒机制可以解决单个生产者和单个消费者的问题,但当生产者或消费者数量大于一时,则会出现新的问题,如果两个生产者进程均被阻塞,则在消费者进程完成后,只会唤醒一个生产者,因为他不知道还有别的生产者处于阻塞状态
信号量
仅用信号来记录只能记录是否有进程处于阻塞态,并不能记录有多少个,因此需要引入信号量
信号量直接使用数字来计算,如果有多个进程阻塞,则每次进入阻塞时数字-1,通过该值可以判断是否还有进程处于阻塞中
信号量定义
如何解决生产者消费者问题
定义三个信号量
full:表示缓存区是否满,消费者开始时申请,消费者如果无法申请得到full,则full–,并且让消费者进入等待,在生产者完成申请后释放,并唤醒该消费者进程
empty:与full相反
mutex:用来表示共享缓冲区,设置初值为1,表示同一时间只能有一个进程进入缓冲区。
2021.11.8
信号量保护
共同操作可能导致empty被改变,但另一个进程没有读到改变后的empty,这样就导致了empty的值更新不正确
临界区
一次只允许一个进程进入的该进程的那一段代码
设计原则
- 互斥进入
- 有空让进
- 有限等待
软件方法
首次尝试-轮转法
在一个进程执行完后,如果另一个进程不执行,则该进程会无法进入,不满足有空让进
标记法
然而如果先经过flag[0] = true,再经过flag[1] = true,则此时,两个进程都会无限进入while循环,无限等待
非对称标记法
Peterson算法
面包店算法
在出现多个进程而非两个进程时,可以采用面包店算法,将每次进程设置为最大值加一,然后反复调用最小num值,调用完后将num值设为0
硬件方法
开关中断
cli()关中断
sti()开中断
中断被关闭,则不会调用schedule
然而此方法在多CPU时不能使用,因为关闭中断只是针对一个CPU,而每次出现中断所有CPU的寄存器均要改变,另一个CPU的中断没有关闭,因此还是会执行schedule
原子指令
死锁处理
多个进程由于互相等待对方持有的资源而造成的谁都无法执行的情况叫死锁
死锁的必要条件
- 互斥使用Mutual exclusion
- 不可抢占No preemption
- 请求和保持Hold and wait
- 循环等待Circular wait
处理方式
- 死锁预防
破坏出现死锁的条件 - 死锁避免
检测每个资源请求,如果造成死锁就拒绝 - 死锁检测+恢复
检测到死锁出现时,让一些进程回滚,让出资源 - 死锁忽略
就好像没有出现过一样
死锁预防的方法
在进程执行前,一次性申请所需的所有资源,不会占有资源再去申请其他资源
缺点:一般来说很难预知需要的事情,编程会比较困难
资源使用率低
对资源类型进行排序,资源申请必须按序进行
缺点:资源浪费
死锁避免
安全状态:如果系统中所有进程存在一个可完成的序列,则称系统处于安全状态
安全序列:上述序列称为安全序列
银行家算法
在每次调用后使用银行家算法进行检测,可以查出是否安全,但这样做耗时太大
死锁检测加恢复
发现问题再进行处理
在死锁后回滚
但是回滚的选择、实现都会比较困难
进程造成的改变很难恢复(回滚)
死锁忽略
由于上述方法都有各自的缺点,因此在个人PC上直接采用死锁忽略