操作系统笔记
ch1.操作系统结构
1.工作控制方式
- 中断(Interrupt)【硬件】
- 陷入(trap)【软件】
2.硬件保护
- 双模式:内核模式(或称系统模式/内核态/系统态)、用户模式(或称用户态)
- I/O保护:特权保护(只在内核模式下才允许执行的指令)和非特权指令
- CPU保护:定时器(设置中断计算机的周期时间,可固定/可变)——CPU Interval
- 内存保护:基址寄存器(存放程序基本地址值)、限长寄存器、越界检测
ch2.进程与线程
1.程序执行方式
程序的顺序执行:顺序性、封闭性、可再现性(只要程序执行环境和初始条件相同,重复执行时结果都将相同)。
程序的并发执行:间断性(每个程序都是“执行-停止-执行”)、失去封闭性(一个程序执行期间,另一个程序可以插入执行)、不可再现性。
2.前趋图
有向无环图。
若两个结点Pi、Pj,仅当Pi操作完成后,才能执行Pj结点的操作,称Pi,Pj之间存在前趋关系。表示为:Pi ->Pj。
集合表示:{(Pi,Pj)|仅当Pi完成,才能执行Pj }
程序Pi和Pj并发执行的Bernstein条件:
(R(Pi) ∩ W(Pj))∪ (R(Pj) ∩ W(Pi))∪(W(Pi) ∩W(Pj))= { }
【注】
R(Pi)={a1,…am}:进程Pi执行其间所需参考的所有变量的集合——读集
W(Pi)={b1,…bn}:进程Pi执行其间所要改变的所有变量的集合——写集
3.进程特征
- 并发性:可并发执行
- 制约性:对资源争用而相互制约
- 结构特征:进程=程序+数据+PCB(进程控制块)
- 动态性:有生命周期
- 独立性
- 异步性:进程按照各自不可预知的速度向前推进
4.进程控制块PCB
主要内容
-
进程标识信息:
(1)进程本身:外标识、内标识(PID)
(2)家族信息:父进程、子进程信息
-
处理机状态信息:中断现场保留区(PSW程序状态字)
-
进程调度信息:状态、优先级、入主存
-
进程控制信息:
(1)程序、数据的外存/内存地址
(2)进程同步和通信机制:消息队列、信号量
(3)资源清单
(4)链接指针
组织方式
-
线性方式:系统中所有PCB都组织在一个线性表中,表的首地址存放在内存专用区
-
链接方式:具有相同状态进程的PCB通过PCB中的链接字链接成一个队列
-
索引方式:系统根据进程状态的不同,建立索引表,根据索引值查找进程。
5.进程的状态
进程的三个基本状态
- 运行状态(Running):指令被执行
- 就绪状态(Ready):进程等待分配CPU
- 阻塞状态(Blocked/Waiting):进程等待事件(I/O事件)的发生
新建状态和终止状态
- 新建状态(New):刚被创建,尚未进入就绪队列时的状态
- 终止状态(Terminated):进程正常/异常结束,移出就绪队列,但尚未撤销时的状态
注意这幅图中状态转换的前驱、后继,比如“等待->运行”、“就绪->等待”状态是不存在的,因为运行只认识就绪状态,等待只认识输入/输出状态。
挂起/解挂状态
设置进程挂起/解挂状态的原因
- 用户需要:中间结果与预期不符;
- 操作系统需要:系统某些功能故障;
- 系统负荷过重
- 父进程请求:修改或协调子进程
- 对换的需要
设置挂起状态后进程状态的转换
- 设置挂起状态后,进程的就绪分为活动就绪(Readya)和静止就绪(Readys)
- 进程的阻塞状态分为活动阻塞(Blockeda)、静止阻塞(Blockeds)
6.进程控制
内核定义
现代操作系统设计采用层次结构,往往将一些与硬件紧密相关的模块或运行频率较高的模块设置在第一层软件中,称为操作系统的内核。
内核基本功能
-
支撑功能:中断处理、时钟管理、原语操作(原语是指一个操作中的所有动作不可分割)
-
管理功能
进程管理:调度、创建、同步等
存储器管理
设备管理
进程创建——进程图
定义:用于描述进程家族关系的有向树。
与前趋图的区别:
- 含义不同:有向树/有向无环图;
- 执行时处理不同:进程图父子进程可同时执行,前趋图父子不可并发执行(即前趋图代表的是执行顺序,进程图代表的是父子关系)。
引发进程创建的事件:
- 用户登录
- 作业调度
- 提供服务
- 应用请求
创建完成的工作:
- 申请空白PCB
- 初始化PCB
- 为新进程分配资源
- 将新进程插入就绪队列
7.线程
定义
进程中的一个实体,是能被系统独立调度和分派的基本单位。在同一进程中,线程的切换不会引起进程切换。线程共享进程的资源,自己不拥有。线程也称轻型进程(Light-Weight Process),进程也称重型进程(Heavy-Weight Process)。
组成
- 线程ID
- 寄存器集合
- 程序计数器
- 栈
多线程的优点
-
系统开销小
-
响应度高:只需要阻塞部分线程,提高了用户的响应
-
资源共享:默认共享所属进程的内存和资源
-
多处理器体系结构的利用
类型
-
内核支持线程KST
又叫内核线程,由内核管理,内核空间里为每个内核支持线程都设置了一个线程控制块(TCB),并通过TCB感知和控制线程。
-
用户级线程ULT
又叫用户线程,对该类线程的创建、切换由用户完成,不利用系统调用。系统未建立该线程的控制块,不知道其存在。
ch3.进程调度
1.调度的类型
- 高级调度:按照一定的算法从后备作业队列中选择满足条件的作业,分配一定资源,创建PCB,入主存就绪队列。
- 低级调度:按照一定的算法从就绪队列中选择满足条件的进程,分配CPU运行。分抢占/非抢占两种形式。低级调度算法简单,目的是不浪费使用CPU资源的时间。
- 中级调度:将在主存中长期得不到执行的进程,按照一定的算法入盘交换区;满足执行条件后再入主存。
2.调度方法选择准则
面向用户(4准则)
- 响应时间
- 请求传到CPU延时
- CPU执行时间
- 结果回传延时
- 周转时间
- 作业从提交到完成所经过的时间,包括后备队列延时、就绪队列延时、CPU执行时间、等待I/O时间。
- 截止时间保证:开始/完成截止时间
- 优先权准则
面向系统(3准则)
- 系统吞吐量
- 单位时间内系统所能处理的任务数越多越好
- CPU利用率
- 各类资源的平均利用
3.调度算法
Process Burst Time
P1 24
P2 3
P3 3
假定进程都是在第0秒到达,顺序如下: P1 , P2 , P3。试计算采用各种算法的平均周转时间和平均等待时间。
先来先服务FCFS
First Come First Serve,思想是选择最先进入后备/就绪队列的作业/进程,入主存/分配CPU。
- 优点:简单、易于实现;公平;
- 缺点:平均等待时间长;短作业不利(使用CPU时间少,等待时间长);紧迫型作业不利。
对于题干,
(1) 平均周转时间: ((24-0)+(27-0)+(30-0))/3=81/3=27
(2) 平均等待时间:((24-0-24))+(27-0-3)+(30-0-3))/3=51/3=17
短作业优先SJF
Shortest Job First,思想是选择后备/就绪队列中执行时间最短的作业/进程,入主存/分配CPU。
优点:平均等待时间最小;系统吞吐量增加;
缺点:长作业不利(长作业饿死现象);需要知道每个作业所需的执行时间。
对于题干,
(1) 平均周转时间: ((30-0)+(3-0)+(6-0))/3=39/3=13
(2) 平均等待时间:((30-0-24))+(3-0-3)+(6-0-3))/3=9/3=3
时间片轮转RR
思想是设置使用CPU时间长度(时间片time slice),就绪队列中的进程依次轮流分配一个时间片的CPU。
优点:平均响应时间短;
缺点:性能依赖时间片的大小。
对于题干,
(1) 平均周转时间: ((30-0)+(7-0)+(10-0))/3=47/3≈15.7
(2) 平均等待时间:((30-0-24))+(7-0-3)+(10-0-3))/3=17/3 ≈ 5.7
优先权调度Priority
思想:选择优先权最高的后备/就绪队列的作业/进程,入主存/分配CPU。
类型:抢占式、非抢占式;
优先权类型:静态优先权、动态优先权。
优点:能处理实时事件(提高对紧迫型任务的响应度)。
进程 到达时间 运行时间 优先权
P1 0 24 4
P2 2 3 2
P3 3 3 3
P4 4 7 1
试计算采用抢占式优先权算法的平均周转时间和平均等待时间。
(1) 平均周转时间: ((37-0)+(12-2)+(15-3)+(11-4))/4=66/4=16.5
(2) 平均等待时间:((37-0-24))+(12-2-3)+(15-3-3)+(11-4-7))/4=29/4=7.25
高响应比优先HRRN
Highest Response Ratio Next,思想:选择响应比最高的后备/就绪队列的作业/进程,入主存/分配CPU。
响应比 Rp=1+(等待时间/要求运行时间)
在该算法中,优先级动态变化,开始是短作业进入进程,随着时间的推移,长作业等待时间逐渐增加,响应比变大,转而长作业的优先级变高。
多级队列调度
Multilevel Queue
思想:系统设置多个就绪队列,分配不同优先权(节省选的时间),先调度优先权高的进程,只有高优先权队列为空时才调度低一级优先权队列中的进程。
多级反馈队列
Multilevel Feedback Queue
思想:
设置多个就绪队列,每队分配不同的优先权;
各队规定一时间片,并各队列随着优先权的降低时间片逐渐增大;
新入就绪队列的进程先入高优先权队列,按FIFO执行规定时间片,未完成,入下一级队尾;
高优先权队列为空,执行次一级队列进程…;
若有新入就绪队列的进程,中止低优先权队列进程执行。被中止进程入本队队尾;
最后一级队列采用时间片轮转;
响应比Rp:Rp=1+(等待时间/要求运行时间)。
4.实时系统调度
调度要求:
- 提供必要的调度信息
- 任务就绪时间
- 开始/完成截止时间
- 处理所需时间
- 资源要求
- 优先级
- 调度方式
- 抢占式
- 有较强的中断处理机构
- 具有快速任务分配机制
- 任务切换开销小
【注】当若干任务是可调度时,可得出结论: ∑ i = 1 n R i T i ≤ 1 → n ( 2 1 n − 1 ) \left. {\sum\limits_{i = 1}^{n}\frac{R_{i}}{T_{i}}} \leq 1\rightarrow n\left( 2^{\frac{1}{n}} - 1 \right) \right. i=1∑nTiRi≤1→n(2n1−1)( R i R_{i} Ri为运行时间, T i T_{i} Ti为周期)。
5.多核系统调度
进程调度与系统结构相关:
- 同构型系统(系统处理能力一致):静态分配、动态分配、自调度
- 异构型系统:主从式
常见调度方式:
- 自调度Self Scheduling
- 成组调度Gang Scheduling:同种作业分配仅在一组执行(针对保存现场的问题),减少调度频率。
- 专用处理机分配
ch4.进程同步和通信
1.进程同步的概念
进程间的关系:
- 间接制约:互斥关系(进程共享资源)
- 直接制约:同步关系(进程之间存在联系——接力赛)
进程同步:
两个进程所表示事件的发生有着某种时序上的关系。
临界资源(Critical Resource):
一次仅允许一个进程使用的资源,如慢速设备、共享变量、数据结构、缓冲区等。
多道系统中,为保证进程并发执行且结果确定,必须互斥使用临界资源。
临界区:
- 定义:进程中访问临界资源的代码段。
- 使用原则:互斥使用,先申请、判断。
- 临界资源的循环进程结构:
While(true){
entry section; //进入区,作判断
critical section; //临界区
exit section; //退出区
remainder section; //剩余区
}
2.进程同步机制应遵循的原则
- 空闲让进
- 忙则等待
- 有限等待:任何进入临界区的进程将会等待有限时间
- 让权等待:放弃CPU时间
3.解决进程互斥问题
软件方法
(1)设置标志位flag[i],flag[j]
标志Pi和Pj进程是否在临界区,初值为false。
Pi:While(true){
while(flag[j]) {no operation};
flag[i]=“true”; //进入区
critical section; //临界区
flag[i]=“false”; //退出区
remainder section; //剩余区
}
(2)设置整型变量turn
标志turn表示哪个进程可进入临界区,flag[i]和flag[j]表示哪个进程要进入,初值为true。
Pi:While(true){
while(turn!=i) {no op}; //进入区
critical section; //临界区
turn=j; //退出区(“谦让动作”)
remainder section; //剩余区
}
- 缺点:当Pj进程还没进去,而Pi进程又想进入却发现无法进入,违背“空闲让进”的规则。
(3)Peterson算法
设置turn表示哪个进程可进入临界区,flag[i]和flag[j]表示哪个进程要进入,初值为true。
Pi:While(true){
flag[i]=true; //表示我(i)想使用该资源(表达意愿)
turn=j; //愿意优先让j使用(表示谦让)
while(flag[j] && turn==j) {no op} //如果j想用而且是自己(i)最后表达的谦让,则等待,否则i先执行
critical section; //临界区
flag[i]=false; //退出区
remainder section; //剩余区
}
Pj:While(true){
flag[j]=true;
turn=i;
while(flag[i] && turn==i) {no op}
critical section; //临界区
flag[j]=false; //退出区
remainder section; //剩余区
}
- 缺点:仍未遵循让权等待的原则(一直while循环消耗资源)。
硬件方法
特点:指令操作只需一个指令周期就能完成(原子性的操作)
(1)检测和设置指令TS(Test-and-Set)
boolean TS (boolean *target) {
boolean rv = *target; //存放lock原来的值
*target = TRUE; //无论之前是否被加锁,都将lock设为true
return rv: //返回lock原来的值,若lock原来未加锁即可进入临界区,否则等待
}
while (true) {
while (TS(&lock)) {no op} //do nothing
critical section; //临界区
lock = FALSE; //退出区,将lock解锁,使别的进程可以进行访问
remainder section; //剩余区
}
(2)Swap指令
void Swap (boolean *a, boolean *b){
boolean temp = *a;
*a = *b;
*b = temp;
}
while (true) {
key = TRUE;
while ( key == TRUE) //直到lock被设为false,则执行lock交换指令后会使得key为true,使得程序可以进入临界区
Swap (&lock, &key );
critical section;
lock = FALSE;
remainder section;
}
4.信号量机制
一些概念
信号量:信号量是一个变量(整数、记录型变量等),可以用一个信号量来表示系统中某种资源的数量。比如系统中只有一台打印机,就可以设置一个初值为1的信号量。
同步原语:P()/wait()操作、V()/signal()操作。
整型信号量机制
semaphore s;
wait(s) : { while (s<=0) {no op;}
s--;
}
signal(s): { s++; }
整型信号量的分类:
二元信号量:s的取值为1和0,初值为1;(mutex:互斥元,如对同一个缓冲区进行操作应采用互斥元进行操作)
一般信号量:用于一般同步,初值为共享资源初始数量。
在实际解决进程同步问题时,可通过前趋图表示出各个进程的前趋关系,然后将各个操作封装,封装后的程序可以并发执行。如下例:
描述(a+b)*(c-d)-e/f:
p1:a+b, p2:c-d, p3:e/f, p4:p1*p2, p5:p4-p3。画出前趋图:
semaphore S1,S2,S3,S4 = 0;
{--
{P1;V(S1);} //V表示释放信号
{P2;V(S2);}
{P3;V(S4);}
{P(S1);P(S2);P4;V(S3);} //P(S1),P(S2)为wait信号
{P(S3);P(S4);P5;}
--}
[注]–应画在”{“上,表示并发。
记录型信号量机制
引入目的:克服整型信号量机制存在的“忙等”(Busy waiting)现象,提高资源利用率。引入结构体semaphore:
typedef struct
{
int value;
struct process *list; //list是进程列表
}semaphore;
P(semaphore s):
{
s.value--;
if (s.value<0) {
insert(caller,s.list);
block(caller);
}
}
若s.value<0,表示系统已无该类资源,申请者阻塞。|s.value|表示该信号量上阻塞的进程数。
V(semaphore s):
{
s.value++;
if (s.value<=0) {
remove(s.list, id);
wakeup(id);
}
}
若s.value<=0,表示尚有进程因等待S代表的资源而处于阻塞状态,所以应唤醒其中之一。
AND信号量集机制
称为同时wait操作,即swait(simultaneous wait),目的是避免死锁。在这里,要么把进程在整个运行过程中所请求的资源全部分配到进程(进程使用完成后一起释放),要么一个也不分配。
死锁原因:
有两个进程A、B。两个临界资源D、E,互斥信号量(初值为1)分别是Dmutex、Emutex。
按下面执行次序,A获得了D等待E,B获得了E等待D,就处于了僵持状态,无外界干预,A、B就陷入了死锁状态。共享资源越多,进程死锁可能越大。
process A: wait(Dmutex); 于是 Dmutex=0
process B: wait(Emutex); 于是 Emutex=0
process A: wait(Emutex); 于是 Emutex=-1 A 阻塞
process B: wait(Dmutex); 于是 Dmutex=-1 B 阻塞
swait(semaphore s[n]):
{
while(true)
{
if ((s[1]>=1) && (s[2]>=1) && …… && (s[n]>=1)) {
for (i=1; i<=n; i++)
{
s[i]=s[i]-1; //访问到资源,全部减1,然后退出,否则等待
break;
}
}
}
}
ssignal(semaphore s[n]):
{
for (i=1; i<=n; i++)
s[i]=s[i]+1;
//唤醒等待s[i]的进程;
}
一般信号量集机制
一次可给进程分配多种临界资源,且同类临界资源一次可分配多个。
swait(semaphore s[n],int t[n], int d[n]):
{
if ((s[1]>=t[1]) && (s[2]>=t[2]) && …… && (s[n]>=t[n])) {
for (i=1; i<=n; i++)
s[i]=s[i]-d[i];
}
else
insert (caller, s[i].list); //插入到不满足的队列中
}
ssignal(semaphore s[n],int d[n]):
{
for (i=1; i<=n; i++)
s[i]=s[i]+d[i];
//唤醒等待s[i]的进程;
}
特殊情况:
-
swait(s,d,d): 不设下限,此时在信号量集中只有一个信号量S,但允许它每次申请d个资源,当现有资源少于d时,不予分配;
-
swait(s,1,1):就是wait(s);
-
swait(s,1,0): 可控开关(当S≥1时,允许多个进程进入临界区;当S=0时禁止任何进程进入临界区)。
5.经典进程同步问题
生产者-消费者问题
特点:一类临界资源,多个,每个临界资源必须互斥使用;
典型示例:有限缓冲区的使用。
【问题描述】
若干生产者、消费者,共享N个缓冲区;
生产者:每次产生一个数据,放入到一个空缓冲区。若无空缓冲区,则阻塞。
消费者:每次从有数据的缓冲区去一个数据,进行使用;若无满缓冲区,则阻塞。
同步:若所有缓冲区皆空,消费者阻塞等待生产者生产;若所有缓冲区皆满,生产者阻塞等待消费者消费。设置一般信号量:
empty表示现有空缓冲区数,初值为M;
full表示现有满缓冲区数,初值为0。
互斥:生产者、消费者对同一个缓冲区的操作必须互斥。设置互斥信号量mutex, 初值:
生产者行为:产生数据,将数据存入到缓冲区;
消费者行为:从缓冲区取出数据,消费数据;
定义变量:buffer[n]、in和out指针。
【注】由于循环队列的特性,实际上会牺牲一个存储单元(必有1个单元为空),队列已满的条件为队尾指针的下一个位置是队头,即(Q.rear + 1) % MaxSize == Q.front
。
伪代码实现方案如下:
semaphore mutex =1,empty=n, full=0;
int buffer [n];
int in=out=0;
{--
Producer:
while (true) {
produce an item in nextp;
wait(empty);
wait(mutex);
buffer[in]=nextp;
in=(in+1) mod n;
signal(mutex);
signal(full);
}
Consumer:
while (true) {
wait(full);
wait(mutex);
nextc=buffer[out];
out=(out+1) mod n;
signal(mutex);
signal(empty);
consume the item;
}
--}
【例】假设有3个并发进程P,Q,R,其中P负责从输入设备上读入信息,并传送
给Q,Q将信息加工后传送给R,R负责打印输出。进程P,Q共享一个有m 个缓冲区组成的缓冲池;进程Q,R共享一个有n个缓冲区组成的缓冲池(假设缓冲池足够大,进程间每次传输信息的单位均小于等于缓冲区长度),请写出满足上述条件的并发程序。
【分析】设置六个信号量empty1、full1、mutex1、empty2、full2、mutex2,empty1表示P、Q共享的缓冲池中空缓冲区数,初值为m;
full1表示P、Q共享的缓冲池中中满缓冲区数,初值为0;
mutex1表示P、Q对同一缓冲区操作的互斥信号量,初值为1;
empty2表示Q、R共享的缓冲池中空缓冲区数,初值为n;
full2表示Q、R共享的缓冲池中中满缓冲区数,初值为0;
mutex2表示Q、R对同一缓冲区操作的互斥信号量,初值为1;
同步描述为:
empty1,empty2,full1,full2,mutex1,mutex2:semaphore=m,n,0,0,1,1;
P: {
while(1){
read a message;
wait(empty1);
wait(mutex1);
store to buffer1;
signal(mutex1);
signal(full1);
}
}
Q: {
while(1){
wait(full1);
wait(mutex1);
get from buffer1;
signal(mutex1);
signal(empty1);
work with massage;
wait(empty2);
wait(mutex2);
store to buffer2;
signal(mutex2);
signal(full2);
}
}
R: {
while(1){
wait(full2);
wait(mutex2);
Get from buffer2;
print;
signal(mutex2);
signal(empty2);
}
}
读者-写者问题
tex2表示Q、R对同一缓冲区操作的互斥信号量,初值为1;
同步描述为:
empty1,empty2,full1,full2,mutex1,mutex2:semaphore=m,n,0,0,1,1;
P: {
while(1){
read a message;
wait(empty1);
wait(mutex1);
store to buffer1;
signal(mutex1);
signal(full1);
}
}
Q: {
while(1){
wait(full1);
wait(mutex1);
get from buffer1;
signal(mutex1);
signal(empty1);
work with massage;
wait(empty2);
wait(mutex2);
store to buffer2;
signal(mutex2);
signal(full2);
}
}
R: {
while(1){
wait(full2);
wait(mutex2);
Get from buffer2;
print;
signal(mutex2);
signal(empty2);
}
}