概述,进程管理
第一章:概述:
1.1,操作系统的主要作用:
- 处理机(CPU)管理:进程控制,进程同步/通信,死锁处理,处理及调度。
- 存储器(内存)管理:内存分配,地址映射,虚拟内存。
- 文件管理(外存/磁盘)管理:文件存储空间管理,目录管理
- I/O设备管理:完成I/O请求,方便用户使用设备,提高利用率,缓冲管理,设备分配与回收,虚拟设备。
1.2,操作系统基本特性:
- 并发:并行--同一时刻。并发--同一时间段内(多道批/时间片流转)
- 共享:互斥共享,同时共享。
- 虚拟:提高资源利用率,
- 异步:多道程序环境下,进程都是停停走走,的方式运行。
1.3,操作系统的运行机制与体系结构
1.3.1,运行机制:
①两种指令:
- 特权指令:如内存清零,开/关中断等。权限很高,影响系统安全性,不允许用户进程使用。
- 非特权指令:+-*/等,不影响系统安全性
②两种处理及状态:
- 核心态(管态):可以执行特权/非特权指令。
- 用户态(目态):只能执行非特权指令。
③两种程序:
- 内核程序:可执行特权/非特权指令,运行在核心态。
- 应用程序:只执行非特权指令,运行在用户态。
1.3.2,操作系统内核:
- 与硬件紧密相关的模块,计算机上配置的底层软件,是操作系统最基本,最核心的部门。
①时钟管理:计时功能
②中断管理:
③原语:具有原子性
④对系统资源进行管理:进程(cpu)管理 / 存储器(内存/外存)管理 / 设备管理
1.3.3,操作系统的体系结构:
①大内核:将操作系统功能作为一个紧密结合的整体放到内核,由于模块共享信息,因此有很高的性能,但维护不便。
②微内核:
- 由于操作系统不断复杂,因此将一部分操作系统的功能移出内核,从而降低内核的复杂性,移出的部分原则划分成
若干服务,相互独立。
- 微内核架构下,操作系统被划分为小的,定义良好的模块,只有微内核这一模块运行在内核态,其余模块运行在用户态。
- 因为需要频繁的在用户态和内核态切换,会有一定的性能损失。
1.4,中断异常
- 为了实现多道程序并发而引入的一种技术
- 当中断发生,意味着操作系统介入开始管理工作,CPU会立即进入核心态
- 中断是CPU从用户态进入核心态的唯一途径
①中断分类:
- 内中断:
陷入:指令中断
故障:硬件故障
终止:软件中断
- 外中断:外设请求,人工干预。
②外中断的处理过程:
- 每条指令执行结束,CPU检查是否有外部中断信号。
- 若有,则暂停当前被中断进程/线程,保存‘现场’。
- 根据指定信号类型,传入相应的中断处理程序。
- 恢复原进程的CPU环境并退出中断处理,返回原被中断进程继续处理。
第二章:进程管理
2.1,进程:
2.1.1,定义:
- 是程序的一次执行
- 是一个程序及数据在处理机上顺序执行所发生的活动,
- 进程是具有独立功能的程序在一个数据集合上运行的过程,他是系统进行资源分配和调度的独立单位。
2.1.2,进程的数据结构:
- 进程实体:包括,程序段,相关数据PCB
- PCB:进程控制块
在多道程序环境下,并发执行程序失去封闭性,具有间断性,结果不可再现性。
为使并发执行的程序(含数据)能独立运行,引入专门的数据结构,PCB。
<1>PCB作为进程实体的一部分,记录了操作系统所需的,用于描述进程当前情况的以及管理进程运行的全部信息
是操作系统中最重要的记录型数据结构。
<2>作用:①使进程可以与其他进程并发,使进程可以间断运行(保存运行状态)。
②提供管理信息。
③提供调度信息(优先级等)
④与其他进程同步通信。
<3>包含信息:①进程标识符(ID)。
②处理机状态(上下文,通用寄存器,指令寄存器,程序状态字,用户栈),用于间断执行,
记录执行状态,恢复现场。
③进程调度信息(优先级等)
④进程控制信息,数据地址,通信机制,资源清单。
2.1.3,进程的基本状态及转换
①就绪态-Ready 已分配到除CPU之外的资源,只有获得CPU就可执行。
②执行态-Running 进程获得CPU正在执行
③阻塞态-Block 正在执行的进程发生事件(IO)或申请内存失败,无法继续执行
2.1.4,进程的控制:
- 实现进程状态的转换
- 修改PCB当中进程状态位并将PCB放到不同队列,(就绪/阻塞)或放入CPU中执行
- 用原语实现。原语通过开/关中断实现原子性(开关中断--特权指令。原语--内核功能)
2.2,进程通信:
①共享存储系统:相互通信的进程共享某些数据结构或共享存储器,互斥访问。
②管道通信:互斥,半双工。一方只能将管道写满才能另一个进程读。只有读完才能写。数据读过之后被抛弃
③信息传递系统:不需要借助共享存储器或数据结构,每个进程都有信息缓冲队列,由系统发送,接收原语实现。
④客户机-服务器系统--如Socket.
2.3,线程:
- 进程的引入,为了使多个程序能并发执行,以提高资源利用率和系统吞吐量。
- 线程的引入,为了减少程序在并发执行时进程切换所付出的时空开销
2.3.1,线程分类
①内核支持线程:
- 线程调度,切换由操作系统内核完成,工作在内核状态。
- 操作系统可见。“内核级线程”才是处理机调度的单位。
②用户级线程:
- 操作系统不可见,从用户角度能看到的线程。
- 用户程序通过线程库来实现,
- 线程切换在用户态下即可完成。
2.3.2,多线程模型:
①多对一:即多个用户级线程映射到一个内核线程。
+:线程切换开销小(不需要操作系统做状态切换),效率高,
-:并发性不高,内核线程阻塞即用户级线程阻塞。
②一对一:一个用户级线程映射一个内核线程
+:并发性高
-:线程切换开销大
③多对多:如4个用户级线程映射到两个内核线程。
2.3.3,线程与进程的关系与区别:
- 关系:①线程是轻量级进程
②进程可以由多个线程组成。
- 区别:①调度:进程是资源分配的单位(操作系统调度的单位),线程是处理及调度的单位。
②资源:进程可以拥有资源,线程除本身TCB(线程控制块)等少量自身资源外不拥有资源,同一个进程
中的多个线程共享进程的资源。
③系统开销:进程切换开销大,线程切换开销小。
④独立性:进程拥有自己的内存空间和其他资源,独立性比共享进程内资源的线程高。
2.4,进程同步:
2.4.1,同步与互斥:
- 同步:多个进程按一定顺序执行
- 互斥:多个进程同一时刻只有一个进入临界区
2.4.2,临界区:临界资源,如打印机,磁带机等需要互斥访问的资源。进程中访问临界资源的代码称为临界区。
- 只要保证进程互斥的进入自己的临界区,便可实现互斥访问临界资源。
2.5,进程同步方法:硬件同步机制,信号量同步机制,管程机制。(单处理机系统进程同步互斥)
2.5.1,同步原则:空闲让进,忙则等待,让权等待。
2.5.2,硬件同步机制:
- 利用特殊硬件指令,对一个字中(state--锁)内容进行检测和修正,或者对两个字内容进行交换。
①关中断:最简单,关闭中断,进程访问临界区时,系统不响应中断,不存在调度。
-:<1>滥用关中断,后果很严重,<2>关中断时间过长,影响系统效率<3>不适于单处理机系统,其他CPU可响应中断
<4>适用于内核进程,不适用于用户进程。
②Test and Set 指令实现互斥
TS指令(即原语)功能描述:
boolean TS(boolean *lock){
boolean old;
old=*lock;
*lock=TRUE;
return old;
}
实现互斥:
do{
while(TS(&lock))
critial section;
lpck:=FALSE;
remainder section;
}while(TRUE);
+:适合多处理机系统
-:不支持让权等待,会一直在while(TRUE)循环里,出现忙等。
③利用Swap指令:
Swap指令描述:
void swap(boolean *a,boolean *b){
boolean temp;
team=*a;
*a=*b;
*b=temp;
}
实现互斥:
do{
key=TRUE;
do{
swap(&lock,&key);
}while(key!=FALSE);
临界区操作;
lock=FALSE;
....
}while(TRUE);
+:适合多处理机系统
-:不支持让权等待,会一直在while(TRUE)循环里,出现忙等。
2.5.3,信号量机制
- 信号量:可以是一个整数,也可以是更复杂的记录型变量,用以表示系统资源的数量。
- 用户进程通过操作系统的一对原语(P,V)对信号量操作,实现继承互斥与同步。
原语:是一种特殊程序段,其执行只能一气呵成,由内核程序通过关中断指令实现。
一对原语:wait(s),signal(s)=>P(s),V(s)
void wait(int s){ void signal(int s){
while(s<=0); s=s+1;
s=s-1; }
}
①信号量分类:
- 整形信号量,只能对信号量执行初始化,P操作,V操作三种。
整形信号量不能实现让权等待,会出现忙等现象。while()一直占着处理机。
- 记录型信号量:
typedef struct{
int value; //剩余资源数
struct process *L; //等待队列
}semaphore;
void wait(semaphore s){
s.value--; //先自减,再锁,value=-1,表示有一个进程再等待
if(s.value<0)
block(s.L); //阻塞原语,挂起进程加入阻塞队列
}
void signal(semaphore s){
s.value++;
if(s.value<=0)
wakeup(s.L); //唤醒
}
②信号量实现互斥:
semaphore mutex=1;
P1(){
....
P(mutex);
临界区代码;
V(mutex);
...
}
P2(){
...
P(mutex);
临界区代码
V(mutex);
...
}
③实现同步:
- 由于异步性,保证多个进程按一定顺序执行
- 即保证”一起一后“,的两个操作
<1>设置同步信号量s=0;
<2>在“前操作”后执行V(s);
<3>在“后操作”前执行P(s);
semaphore s=0;
P1(){ P2(){
代码1; P(s);
代码2; 代码4; =>即保证代码4在代码2之后执行。
V(s); 代码5;
代码3; 代码6;
} }
④生产者-消费者问题:
- 先分析问题中的“同步”与“互斥”关系
<1>各个进程互斥访问缓冲区 --互斥
<2>缓冲区满,生产者阻塞 --同步
<3>缓冲区空,消费者阻塞 --同步
semaphore mutex=1;
semaphore empty=n;
semaphore full=0;
producer(){
while(1){
生产产品;
P(empty); //缓冲区满了(empty<0)阻塞
P(mutex); //没满,上锁。
将产品放入缓冲区
V(mutex);
V(full);
}
}
consumer(){
while(1){
P(full); //缓冲区空则阻塞
P(mutex); //非空,上锁。
从缓冲区取出一个产品
V(mutex);
V(empty);
使用产品;
}
}
⑤多生产者-消费者问题
- 一个盘子只能放一个水果。爸爸放苹果,妈妈放橘子,女儿 吃苹果,儿子吃橘子。
- 分析 同步 与 互斥
<1>盘子:互斥访问,盘空才能放水果 互斥
<2>父亲--女儿 同步
<3>母亲--儿子 同步
semaphore plate=1;
semaphore apple=0;
semaphore orange=0;
dad(){
while(1){
P(plate);
放苹果;
V(apple);
}
}
mom(){
while(1){
P(plate);
放橘子;
V(orange);
}
}
son(){
while(1){
P(orange);
拿橘子;
V(plate);
吃橘子;
}
}
daught(){
while(1){
P(apple);
拿苹果;
V(plate);
吃苹果;
}
}
⑥吸烟者问题:
- 生产者生产3种资源,3个消费者伦流消费不同资源
- 缓冲区大小=1;
- 同步 与 互斥:
<1>缓冲区 互斥
<2>资源1--消费者1, 同步
<3>资源2--消费者2 同步
<4>资源3--消费者3 同步
semaphore offer1=0;
semaphore offer2=0;
semaphore offer3=0;
semaphore finish=0;
int n=0;
producer(){
while(1){
if(n==0){
放资源1;
V(offer1);
}else if(n==1){
放资源2;
V(offer2);
}else if(n==2){
放资源3;
V(offer3);
}
n=(n+1)%3;
P(finish);
}
}
consumer1(){
while(1){
P(offer1);
拿资源1;
用资源1;
V(finish);
}
}
consumer2(){
while(1){
P(offer2);
拿资源2;
用资源2;
V(finish);
}
}
consumer3(){
while(1){
P(offer3);
拿资源3;
用资源3;
V(finish);
}
}
⑦读写者问题
- 读进程共享
- 写进程互斥
- 读进程共享--第一个都进程上锁,最后一个读进程下锁
<1>
semaphore rw=0;
int count=0;
write(1){
while(1){
P(rw);
写文件;
V(rw);
}
}
readd(){
while(1){
if(count==0) //
P(rw); // 检测和赋值,不能“一气呵成”,多线程下会出错。
count++; //
读文件;
count--; //
if(count==0) //检测和赋值,不能“一气呵成”,多线程下会出错。
V(rw); //
}
}
<2>修改“一气呵成” 再增加一个信号量,保证原子性。
semaphore rw=1;
int count=0;
semaphore mutex=1;
read2(){
while(1){
P(mutex);
if(count==0)
P(rw);
count++;
V(mutex); =>读进程非常多的情况下写进程会饥饿,
读文件....
P(mutex);
count--;
if(count==0)
V(rw);
V(mutex);
}
}
<3>解决写饥饿-- 读写公平法
semaphore rw=1;
int count=0;
semaphore mutex=1;
semaphore w=1;--读写公平;
write(){
while(1){
P(w);
P(rw);
写文件...
V(rw);
V(w);
}
}
reader(){
while(1){
P(w); //如果有写进程再等待,该都进程会等待写进程执行完;
P(mutex);
if(count==0);
P(rw);
count++;
V(mutex);
读文件...
P(mutex);
count--;
if(count==0)
V(rw);
V(mutex);
}
}
⑧哲学家进餐问题
- 每个进程要同时持有两个临界资源才能进行下一步。
- 循环等待--死锁
- 解决方案:
<1>最多只允许N-1个哲学家同时拿筷子;
<2>奇数号先拿左筷子,偶数号先拿右筷子;
<3>只有两边筷子都空闲才允许拿筷子
当哲学家左右筷子都空闲才拿:
semaphore chopstick[5]={1,1,1,1,1};
semaphore mutex=1;
P1(){
while(1){
P(mutex);
P(chopstick[i]);
P(chopstick[(i+1)%5]);
V(mutex);
吃饭;、
V(chopstick[i]);
V(chopstick[(i+1)%5]);
}
}
2.5.4,管程机制
- 信号量虽方便,但每个要访问临界区的进程都要自己写wait().signal(),不利于编程,使大量同步操作分散在各个进程中,同步不当引发死锁。
- 将同步封装起来(类),由代表共享资源的数据结构以及对该数据实施操作的一组过程所组成的资源管理器程序共同构成了一个操作系统的
资源管理模块;
①进程与管程的区别:
<1>设置进程是为了实现系统并发性,设置管程是为了解决共享资源的互斥使用问题。
<2>进程调用管程。进程为主动工作方式,管程为被动工作方式。
<3>进程之间可并发执行,管程不能与其调用者并发执行
<4>进程具有动态生命周期,管程则是操作系统中的一个资源管理器模块。
②java中synchrobized关键字修饰函数也保证一段时间内只能被一个线程调用。
2.6,进程调度
- 非抢占式
- 抢占式
2.6.1,进程调度的目标:
①提高资源利用率
②公平
③平衡
④策略强制执行
2.6.2,进程调度的任务:
①保存处理机的现场信息,如程序计数器,多个通用寄存器
②按某种算法选取进程
③把处理机分配给选取的进程
2.6.3,分类:
①高级调度
- 作业调度,按一定原则从外存上处于后备队列的作业中选取一个。 一个作业只调入一次
- 是外存(辅存)于内存之间的调度
②中级调度
- 引入虚拟内存技术,暂时不运行的进程掉至外存。内存空闲了再调入内存
- 调到外存的进程属挂起状态,但PCB会留在内存挂起队列中。
- 内存调度,一个进程可能多次调入调出内存。
③低级调度
- 进程调度,实现进程并发,从内存就绪队列中选取一个进程分配处理机。
- 内存与CPU之间的调度
2.6.4,调度算法:
- 批处理系统
①FCFS,先来先服务,按任务到达的时间先后顺序进行调度。
②SJF,短作业优先,占用处理机时间短的进程先执行。
-:<1>必须先预知运行时间,<2>对长作业不利<3>不能保证紧迫的作用得到及时处理
③优先级调度:根据紧迫程度给进程制定优先级,FCFS中按等待时间,SJF按运行时间为优先级
④高响应比优先:优先级=(等待时间+需要的处理时间)/需要服务的时间 = 响应时间/需要服务的时间。
- 交互式系统
①时间片流转法:
- 按FCFS排列,依次执行一个时间片大小。
- 执行结束或时间片用完引发调度(低级调度)
- 时间片的长度影响效率:太大:切换太频繁,效率低。太小:实时性无法保证。
②优先级调度算法
- 类型:抢占/非抢占式
- 优先级类型:
<1>静态:进程类型。进程对资源的的要求(SJF,要求少,先执行)。用户要求(用户急需)
<2>动态:动态调整。进程等待长了,提升优先级。进程占用处理机很长时间,降低优先级。
--如高响应比优先调度。
③多级反馈队列--抢占式
- 设置多个队列,按优先级划分,第一个队列优先级最高,但时间片最短。
- 每个队列按FCFS算法调度
- 队列之间按优先级算法调度,仅当第一个队列空才调用第二个队列。依次类推,当第i层中某进程再执行,又有
新进程申请处理机,则将当前进程暂停放回原i队列尾部。CPU给新进程(第一层优先级高,,抢占式)。
- 实时系统
①最早截至时间优先算法:EDF,deadline
②最低松弛度优先算法:松弛度=截止时间-所需时间-当前时间
2.7,死锁:
2.7.1,死锁的必要条件:
①互斥条件: 互 在一段时间内某资源只能被一个进程占用。
②不可抢占条件 不 对已持有的资源,即使他其他资源申请被阻塞,也不释放已有资源。
③请求保持条件: 请(侵) 进程以获取资源,在未使用完之前不能被抢占。
④循环等待条件 环(犯) 哲学家进餐问题中循环等待。
2.7.2,死锁预防:
- 破坏死锁的必要条件
- 互斥条件是非共享设备必须的,无法破坏
①破坏“请求保持条件”:允许进程只获得前期的资源便可运行。运行过程中再逐步释放已分配给自己的已用完的全部资源,再申请新的资源。
进程一次申请全部资源,
②破坏“不可抢占条件”:当一个已拥有某些不可抢占资源的进程因为新申请资源被阻塞时,需要释放已有的资源。
③破坏循环等待条件: 对系统资源进行线性排序,每个进程必须按顺序申请资源。
2.7.3,避免死锁:
- 银行家算法。