操作系统概述
pocc(计组)
summary_2018/11
1、常见的基本概念
1.1、并发/并行
- 并发指的是在某一时间段有多个程序能够同时执行。但在某一时间点只有一个程序在执行。
- 并行指的的是在某一时间点多个程序同时执行。
- 并发是伪并行,而并行是真正的并行;并发可以通过进程、线程的切换实现,但并行依赖于硬件才能够实现,比如多核CPU,才能真正意义上实现。
1.2、共享
- 指系统中的资源能够被多个进程、线程使用;可以分为同时共享、互斥共享。
- 互斥共享指的是比如打印机等资源,在同一个时刻只能被一个进程占有,需要使用同步机制来实现对临界资源的访问。
1.3、虚拟技术
- 虚拟技术在操作也经常使用,用于把一个物理实体转化为更多的逻辑实体,主要有时分复用技术、空分复用技术两种。
- 时分复用技术:比如多个进程使用单核处理器实现并发就运用了时分复用技术,通过把CPU的时间划分为多个时间片,给队列中的进程、线程使用,实现并发。
- 空分复用技术:主要在虚拟内存中使用,将物理内存抽象为地址空间,每个进程都有自己的地址空间,但是程序运行时,并不需要所有的页都在物理内存中,当发生缺页时,通过置换技术,将程序运行需要的页换入物理内存中。
1.4、异步
- 这里的异步是指一个程序从执行开始到结束不是连续的,而是中间进入过阻塞态(比如等待I/O、暂停),走走停停完成的。
2、功能模块
2.1、进程管理
2.2、内存管理
2.3、文件管理
2.4、设备管理
3、进程管理
3.1、进程与线程
- 概念
- 进程是资源分配的基本单位。进程控制块(Process Control Block, PCB) 描述了进程的基本信息、状态信息,对进程的的操作其实就是对PCB的操作。
- 线程是独立调度的基本单位。一个进程可以有多个线程,线程共享进程的一些资源,当然也有自己的一些独立资源。
- 区别
- 1、资源:进程是资源分配的基本单位,线程是调度的基本的基本单位,共享隶属进程的资源。
- 2、调度:线程是独立调度的基本单位,同一个进程内的线程调度不会引起进程的切换;但是跨进程的线程调度则会引起进程的切换。
- 3、通信:同一进程内线程通信可以通过共享变量实现,而进程的通信则需要IPC、管道等完成。
- 4、系统开销:进程的创建和撤销,系统都要为其分配和回收资源,比如内存空间等等;而线程的创建和撤销只需要很少的处理,比如寄存器的处理,相比进程的切换开销小得多。
- 5、健壮性:进程的的崩溃不会引起其它进程,健壮性更强;而线程的崩溃则会引起整个进程的崩溃。
3.2、进程生命周期
- 进程的生命周期可以分为:创建、就绪态、阻塞态、运行态、终止五个阶段,如下图所示,只有运行态与就绪的之间是双向的,其它过程都是单向的的,不可逆的。
- Tips:阻塞态是由于等待I/O、等待事件完成产生的;CPU时间片用尽会回到就绪态等待。
3.3、进程调度
- 进程调度算法用于使进程能够按照我们的需要去运行;比如批处理系统需要保证吞吐量和周转周期,交互系统需要保证实时性。因此对于不同的系统,我们会采用的不同的调度算法。
- 先来先服务 first-come first-serverd(FCFS)
- 按照请求的顺序运行。不利于短作业,因为当前面有长作业时,会造成长时间的等待。
- 短作业优先 shortest job first(SJF)
- 按运行时间长短进行调度。有利于短作业,长作业可能产生饥饿。
- 最短剩余时间优先 shortest remaining time next(SRTN)
- 优先级调度
- 为所有进程分配优先级,根据优先级进行调度。为了防止优先级较低的进程陷入饥饿状态,可以根据时间的推移增加进程的优先级。
- 时间片轮转
- 将cpu的时间分为多个时间片,每次调度时将一个时间片分配给进程等待队列的首进程,当消耗完时,将该进程放到队列末等待下次调用。
- 该调度算法中对时间片的划分十分重要,太短会导致过多的上下文切换;太长那么就不能保证交互系统的实时性。
- 多级反馈队列
- 当一个进程需要100个时间片,那么它将需要进行100次调度。对于这种需要长时间片的作业是十分不友好的,因此我们引入了多级反馈队列调度算法。1、设置多个队列,每个队列的时间cpu长度依次增大:1,2,4,8,16...那么上面需要100次调度进程则只需要7次调度。2、与此同时,每个队列的优先权也不同,时间片最小的优先权最高,只有当上一个队列没有进程在排队时,才能执行下一个队列。
3.3、进程同步
3.3.1、互斥、同步、临界区
- 互斥:指的是系统中的资源不能被多个进程同时访问,只能由单个进程占有,比如打印机。
- 同步:指多个进程在某些地方不能同时运行,需要按一定的顺序运行。比如对互斥资源的使用,因此互斥可以看作同步中的一种特殊情况。
- 临界区:对临界资源(互斥)进行访问的那段代码称为临界区。为了互斥访问临界资源,每个进程进入临界区时都要进行检查。
entry section // 检查并标志
critical section // 执行临界区代码
exit section // 清除设置的标志
3.3.2、实现方式
- 信号量
- 信号量,一个用于表示资源的变量n(比如0、1的互斥变量),用于解决同步/互斥的一种机制,可以对其进行down、up操作, 也就是我们常说的P、V操作。
- down:若n>0,n-1,执行进程;若n<=0,进程陷入睡眠,等待n>0。
- up:当进程运行完后,需要释放资源,需要进行up操作,n+1,让其它睡眠的进程唤醒。
// 伪代码
var semaphore;
while(true){
down(semaphore);
//临界区
up(semaphore);
}
- 管程
- 通过信号量的方式去实现进程对互斥资源的访问,实现同步操作,需要客户端做较多的操作。而管程把这一部分控制代码分离出来,形成一个独立的模块,使客户端调用更加方便。
- 在同一个时刻只能有一个进程持有,当不在使用对应资源时,应该释放资源让其它进程持有。一般通过wait()、signal()操作实现同步操作。
- wait():检查资源是否可用,如果可以则进程执行;否则阻塞进入队列;
- signal():表示进程将要释放资源,并唤醒等待队列中的进程;
// 伪代码
// Monitor 管程
// enter()函数检查能否获得管程
// leave()释放管程
while(true)
{
Monitor. enter();
// 临界区
Monitor. leave();
}
3.3.3、经典同步问题(信号量)
- 生产者和消费者问题
- 生产者和消费者共同维护一个缓冲区,消费者不能从空的缓冲区中取数据,生产者不能向已满的缓冲区家数据。
思路:
1、首先对于互斥资源缓冲区设置一个信号量mutex=1
2、缓冲区的大小:empty=n,生产者使用;full=0,消费者使用
// 伪代码
producer(){
while(true){
down(empty);
down(mutex);
// 生产 empty-n
up(mutex);
up(empty);
}
}
comsumer(){
while(true){
down(full);
down(mutex);
// 消费 full-n
up(mutex);
up(full);
}
}
mian(){
comsumer(),producer();
}
Tips:
1、这里不能先对缓冲加锁,再测试empty、full,因为比如当生产者获取到缓冲区资源时,此时empty为空,生产者进入睡眠,而消费者也无法获得缓冲区,永远等待下去。
2、更复杂的情况,比如多个消费者,多个生产者,大家也可以思考一下
- 读者写者问题
- 允许多个进程进行读数据,但是不允许读写、和写写操作同时发生
思路:写优先,当一直有读者读,写者可能产生饥饿
1、读写不能同时进行,设置一个变量mutex,表示数据库互斥
2、当一个读者时,需要获取数据库,用rcount计数,rcount=1表示第一个读者,当recount>1,表示有读者在进行,可以直接读,但由于不能让两个进程同时操作rcount,因此也是互斥的
// mutex 表示数据库互斥
// rcount 表示当前正在读的人数
// 伪代码
writer(){
while (true){
down(mutex)
// write
up(mutex)
}
}
reader(){
while (true){
// 防止多个进程同时操作recount变量
down(rcount)
rcount++
if(rcount==0) // 当第一个读者时,需要获取数据库资源
down(mutex)
up(rcount)
// read
down(rcount)
rcount--
if(rcount==0)
up(mutex)
up(rcount)
}
}
- 哲学家进餐问题
- 五个哲学家围着一张圆桌,每个哲学家面前放着食物。哲学家的生活有两种交替活动:吃饭以及思考。当一个哲学家吃饭时,需要先拿起自己左右两边的两根筷子,并且一次只能拿起一根筷子。
3.4、进程通信
4、死锁
4.1、死锁、活锁、饥饿
- 死锁:两个或多个进程因抢占互斥资源而造成的相互等待状态,没有外力作用,无法再执行进行下去。
- 活锁:虽然两个进程都可以获得资源,由于相互谦让,两者都不能获得资源的现象。
- 饥饿:由于进程调度算法,导致某些进程一直不能获得资源,而等待的现象。比如采用优先级调度算法时,一直存在比T1进程优先级高的进程,那么T1将一直不能获得资源而等待。
4.2、死锁的条件
- 互斥:两个或者多个进程使用的资源是互斥的。
- 请求与保持:已经获得某个资源的的进程可以请求新的资源。
- 不可剥夺:进程已经获得的资源不可被强制剥夺。
- 环路等待条件:两个或者多个因请求互斥资源而形成的一种环路等待状态。
4.3、死锁的处理方案
4.3.1、驼鸟算法
- 像鸵鸟一样,把头埋在沙子里面假装什么都没有发生。因为死锁发生的概率很低或者对用户的影响很小,而处理的代很高,因此在有些时候我们可以假装没有发生。
4.3.2、死锁检测与死锁恢复
- 1、死锁检测
// 1.1、每种资源只有一个的情况,如上图所示:方块表示进程、圆圈表示资源
思路:这种情况我们可以通过查找资源形成的有向图,通过深度遍历,查找有向图中是否有环,如果存在环则形成了死锁。
// 1.2、每种资源有多个的情况
E:每种资源总量
A:每种资源剩余量
C:进程已经占有的资源
R:进程执行还需要的资源
思路:通过依次检查每个进程还需要的资源,如果能够从A中取出足够的资源,则让进程获取资源执行,并释放资源让其它进程获取并执行完所有的进程,则没有发生死锁;如果不满足,则进程无法获得资源执行完成,说明造成了死锁(若无进程获得资源则是资源不足)
1、杀死进程,释放部分资源
2、通过抢占资源实现
3、回滚进程,回滚到没有发生死锁前
4.3.3、死锁避免
- 在程序运行的时候避免死锁的发生,分配资源时检查是否安全来避免,比如使用银行家算法。
- 安全状态
上图矩阵的含义:
矩阵第一列:进程名
矩阵第二列:进程已经拥有的资源
矩阵第三列:进程执行总共需要的资源
free:表示还可以分配尚未分配的资源
是否安全的定义:如果能够找到一种分配顺序,让进程依次获得资源,并完成执行,我们说这个状态是安全的;如果找不到,则不安全。
比如a图可以按照B->C->A的顺序执行完,则说明是安全的。
因此:若需要避免死锁的发生,则需要在分配资源时避免进入这种不安全的的状态。
- 1、单个资源银行家算法
一个银行家承诺向他的客户发放一定贷款的额度,为避免进入死锁,如果请求会导致进入不安全状态,则拒绝请求,等待其它请求。
比如按照上图的方式发放,银行家将进入C状态,而C是不安全状态;因此,银行家会拒绝C之前的请求,避免进入C的状态。
- 2、多个资源银行家算法
多个银行家算法与单个银行家算法相似,只是发放的不只是贷款,可能还有食物等其它资源;
E:表示总资源
P:表示已经分配分资源
A:还可以分配的资源
算法:每次分配时,检测分配该资源后是否会进入不安全状态;如果会进入不安全状态,则拒绝分配;否则则分配资源。
4.3.4、死锁预防
- 预防死锁的发生,可以通过破坏死锁形成了四个条件来避免。
1、互斥条件:一般无法,因此资源互斥本身无法改变
2、不可抢占条件:可以抢占,比如高优先级抢占低优先级
3、请求与保持条件:获取资源时,要么一次性获取,要么一个也不获取
4、循环等待条件:对资源编号,按顺序获取资源,从而不会形成环
5、内存管理
- 虚拟内存是为了将物理内存扩大成更大的逻辑内存,从而能够运行需要更大内存的程序。
- 操作系统将物理内存抽象为地址空间,称为物理地址空间;而程序也会拥有自己的地址空间,这些地址空间称为虚拟地址空间;虚拟地址空间部分映射到物理地址空间上。程序执行时并不需要把所有的地址空间装入内存,当使用没有映射到物理地址空间的虚拟空间地址时,会发生缺页中断,操作系统会将这部分的内容调入内存并重新执行失败的指令。映射关系如下图所示:
- 主要包括以下几种方式:分页式存储、分段式存储、段页式存储。
5.1、分页式存储
5.1.1、概述
- 分页式存储系统将地址空间分为一个固定大小的页,而物理地址和虚拟地址的映射通过页表实现。如下图所示:页表中有16个页,需要通过4个比特位来定位,比如虚拟地址为0010 000000000100,读取前4=2,则在页表中的索引为2,读取内容为110 1,最后一位表示是否在内存中,110表示物理空间地址(此时页在内存中);虚拟地址后12位表示在页中的偏移位置。因此该虚拟地址的物理地址空间为:110 000000000100。
5.1.2、页面置换算法
- 当程序要访问的页不在内存中就会发生缺页中断,此时就需要将内存中的页换出为需要访问的页腾出空间。而页面置换算法的目的就是使换入换入的频率更低,即缺页的概率更小。
- 1、最优替换算法(OPT, Optimal replacement algorithm)
即把最长时间不会访问的页换出,只是一种理论上的算法,因为无法知道一个页多长时间不再不再被访问。
比如页的访问序列:7012105231517
物理内存页只有三个:701、7最长不会不会使用,因此应该把7换出
- 2、最近最长时间未使用(LRU, Least Recently Used)
虽然无法知道将来的使用情况,但是可以却可以知道过去的使用情况,一般来说很长时间未使用的页将来使用的概率的也不大,我们可以换出这些页;
实现方式:如下图所示:通过维护一个链表实现,每次使用的页放在表头或者移动到表头,这样就可以保证在链表尾部的页即是最近最少使用的页。但需要每次更新链表,代价比较大。
- 3、最近未使用算法(NRU, Not Recently Used)
为每个页设置两个状态位,R(0,访问时置为1),M(0,修改时置为1),R位会定时清0:
页面1:R:0,M:0
页面2:R:0,M:1
页面3:R:1,M:0
页面4:R:1,M:1
每次替换时,换出编号最小的页。比如上述的编号的为00的页面1替换出去,该页既没有被访问也未修改,被换出。而以页面2编号01为例,修改了但最近没访问,可能会被再次访问,相比与页面3编号10访问了还未修改,更有可能被换出。
- 4、先进先出(FIFO, First In First Out)
即维护一个队列,换出那些先进入的页。而频繁使用的页也会被换出。
为了避免FIFO算法将频繁使用的页换出去,第二机会算法会检查页的R位,如果为0,那么这个最早进来又没有使用的页将会被换出;如果R位为1,那么R位将置0,并放入队列尾。
思路:每次替换只需从队列头查找R=0的页换出即可。
第二次机会算法还有一个缺点就是需要在链表中移动页面,而时钟算法通过形成一个环,通过移动指针来避免移动,当遇到需要移动的情况,只需要指针前移即可。(即指针指向的是队列头)
5.2、分段式存储
5.3、段页式存储
6、磁盘
6.1、概述
- 磁盘是计算机中用于存储信息的一种常见设备,利用磁记录技术在涂有磁记录介质的旋转圆盘上进行数据存储的辅助存储器。
1、盘面(Platter):一个磁盘可以由多个盘面组成。
2、磁道(Track):一个盘面由多个磁道组成,即盘面上不同半径的环道。
3、柱面(Cylinder):在由多个盘面构成的磁盘中,多个盘面同一半径磁道构成的圆柱面。
4、扇区(Track Sector):磁道上的一个弧段,是最小的存储单元。
5、磁头(Head):从盘面上读取、写入信息的部分,能够将磁信息转化为电信息(读)、或者将电信息转换为磁信息(写)。
6、制动手臂(Actuator arm):移动磁头的手臂。
7、主轴(Spindle):旋转整个盘面。
6.2、磁盘调度算法
1、寻道时间:将磁头移到数据所在的磁道。
2、旋转时间:将磁头移到数据所在的扇区。
3、数据的传输时间。
其中:寻道时间占用时间最长,而磁盘调度算法的目标是使平均寻到时间最短。
6.2.1、先来先服务(FCFS, First Come First Served)
- 按照请求顺序进行调度。优点是公平,但却未做任何优化。
6.2.2、最短寻道时间优先(SSTF, Shortest Seek Time First)
- 优先满足请求中离当前磁道最近的请求,可以使平均寻道时间降低。但是却不公平,如果总出现离当前磁道较近的请求,可能会造成离当前磁道较远的请求产生饥饿。
6.2.3、电梯算法(扫描算法,SCAN)
- 最短寻道时间优先虽然可以降低平均寻道时间,但却会造成饥饿现象;电梯算法解决了这个问题,总往一个方向走,直到这个方向没有请求。