前言
进程管理模块是操作系统中非常重要的部分。进程的调度算法,进程通信(PV操作,这好难!!),进程与线程的区别联系等等,需要仔细认真地学习,联系之前学的知识点,建立知识点的思维导图。
概要
多道程序设计、进程、线程、进程间的同步与互斥、进程调度、系统核心
1.多道程序设计
背景:一般程序是顺序执行的,这与人类的认知规律相符。试想,大多数人是不是偏好按顺序做事情,有条不紊。那程序的顺序执行有什么特点呢?
- 顺序性:处理机严格按照程序所规定的顺序执行,每个操作必须在下一个操作之前结束。
- 封闭性:程序一旦开始执行,那计算结果不受外界的影响,当程序的初始条件给定后,其后的状态只能由程序本身确定,只有本程序才能改变它。(就好比我按顺序做某件事情当然是不希望有其他的事情打捞到我啦)
- 程序执行结果的确定性:程序执行结果与时间无关性。(做的慢和做的快,只要我按程序的步骤一步一步执行,那么最后的结果是一样的,例如(2+3)*3+7,计算这个式子按顺序计算,慢点快点结果都是一样的)
- 程序执行结果的可再现性:如果程序在不同的时间执行,只要输入的初始条件相同,无论何时重复执行该程序都会得到相同的结果。
引入:顺序执行的资源利用率很低,早上起来刷牙和听早间新闻明明可以同时进行,但是非得按顺序先刷牙再听新闻,或是听完新闻再吃早饭。如果是我,我已经饿晕过去了。所以,为了提高计算机系统中各资源的利用效率,缩短作业的周转时间,在现代计算机中广泛采用多道程序技术,使多种硬件资源能并行工作。
多道程序设计特点:
- 独立性:每道程序在逻辑上是独立的,且执行速度与其他程序无关,执行的起止时间也是独立的
- 随机性:在多道程序环境下,程序和数据的输入和执行开始时间都是随机的。(因为你不知道之前做完哪个操作转换过来的,比如你是从刷完牙来的,也有可能是听完新闻来的)
- 资源共享性:输入输出设备、内存、信息等资源都会被各个程序共享。(你同时在听新闻和吃饭,不就是在共享你自己吗)
2.进程
2.1进程的概念
进程是具有独立功能的程序关于某个数据集合上的一次运行活动(妙哉,既表明了进程是程序+数据,有表明了进程是动态的,程序是静态的),是系统进行资源分配和调度的独立单位。
2.2程序和进程的区别
- 进程能更真实地描述并发,而程序不能
- 程序是静态的,进程是动态的
- 进程有生命周期,有生有死,而程序是相对长久的
- 一个程序可对应多个进程,反之亦然
2.4进程分类
- 系统进程
- 用户进程
- 前台进程
- 后台进程
- CPU密集型进程
- I/O密集型进程
2.5 进程的基本状态及其转换
七状态进程模型
- 就绪模型(Ready):进程在内存且立即进入运行状态
- 阻塞模型(Blocked):进程在内存等待某事件的出现
- 阻塞挂起状态(Blocked,suspend):进程在外存并等待事件的出现
- 就绪挂起状态(Ready,suspend):进程在外存,但只要进入外存,即可运行
- 挂起(Suspend):把一个进程从内存转到外存
- 阻塞->阻塞挂起:没有进程处于就绪状态或就绪进程要求更多的内存资源
- 就绪->就绪挂起:有高优先级级阻塞进程(系统认为会很快就绪的)和低优先级就绪进程时,系统会选择挂起低优先级就绪进程
- 运行->就绪挂起:对抢占式系统,当有高优先级阻塞挂起进程因事件出现而进入就绪挂起,系统可能会把运行进程转到就绪挂起状态。
- 激活(Active):把一个进程从外存转到内存
- 就绪挂起->就绪:没有就绪进程或就绪挂起进程优先级高于就绪进程
- 阻塞挂起->阻塞:当一个进程终止结束后并释放足够多内存时,系统会把一个高优先级阻塞挂起(系统认为会很快出现所等待的事件)进程调入内存。
为什么会发生挂起?
内存不够了;用户希望挂起一个程序的执行,以便进行调试或关联资源使用;父进程请求
2.6进程控制块(PCB)
是什么(what):系统为了管理进程设置的一个专门的数据结构,用它来记录进程的各种属性,描述进程的动态变化过程。
为什么要有(why):系统利用PCB来控制和管理进程,所以PCB是系统感知进程存在的唯一标志。
so:进程与PCB是一一对应的
2.6.1PCB表的组织方式
- 线性方式
- 链接结构
- 索引结构
- 进程队列
2.7进程控制
其实是进程的状态间的转换,由原语完成
原语:完成某种特定功能的一段程序具有不可分割性或不可中断性
- 创建原语:出生,填PCB表
- 撤销原语:死亡,撤销PCB表
- 阻塞原语:运行->阻塞
- 唤醒原语:阻塞->就绪
3.线程模型
3.1引入
为什么会提出线程这个概念呢,进程不是资源调度和分配的基本单位吗,还需要线程啥事。不急,我们一步一步来分析:
首先,先复习一下进程的的两个基本属性:可拥有资源的独立单位;可独立调度和分派的的基本单位。正是它有了这两个属性,所以能够并发执行。但是,程序能够并发执行系统要做下面这些事:
- 创建进程:必须为其分配所需的所有资源(除了CPU,包括内存空间、I/O设备)还要建立相应的数据结构PCB
- 撤消进程:回收资源,撤销PCB
- 进程切换:在对进程进行切换的时候,要保留当前的CPU环境和设置新选中进程的CPU环境,这可要花不少CPU时间。想想,CPU的使用时间计费,看着都心疼。
要是把两个属性分开来,作为调度和分派的基本单位,轻装运行;作为拥有资源的基本单位,不频繁地进行切换。
3.2线程
what:线程是进程中的一个实体,是CPU调度和分派的基本单位。线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源。
3.2.1 线程的属性(简要)
- 状态转换:执行、就绪、阻塞
- 共享所在进程的地址空间和其他资源
3.2.2 引入线程的好处
- 新建和撤销一个进程所花费的时间少
- 两个线程切换花费的时间少
- 同一进程内的线程共享内存和文件,因此他们之间相互通信不需要调用内核
3.2.3 线程和进程的比较
不详细说了
调度;并发性;拥有资源;系统开销
3.2.4 线程的实现机制
有两类线程:核心级线程KLT、用户级线程ULT
主要区别是是否内核参与了
4.系统内核
what:为了有效地控制和管理进程的运行,操作系统中必须设置一个统一的机构,提供支持进程运行的各种基本操作,这叫做系统内核。简称内核。
向上提供多个无中断的虚拟机器,在核心中不允许中断。
how:进入核心的唯一入口:中断
内核执行的特点:
- 内核执行是连续的
- 内核执行过程中在中断屏蔽状态下
- 内核使用特权指令
5.进程调度
5.1批处理系统的进程调度算法
- 先进先出(FIFO):非抢占
- 最短作业优先(SJF):非抢占
- 最短剩余时间优先(SRTN):抢占
- 最高响应比优先(HRRN):非抢占
5.2 交互式系统的调度算法
- 时间片轮转算法(RR):抢占
- 最高优先级算法(HPF):抢占
- 多级反馈队列:时间片和优先级互补,抢占
- 最短进程优先(SPN):非抢占
6.同步与互斥
概要
- 进程互斥
- 进程同步
- 信号量与PV操作
- 经典IPC问题
6.1进程间的相互作用
有关系的进程称为相关进程,没有关系的称为无关进程
多道程序系统中并发运行的进程之间存在着相互制约关系,这种相互制约的关系称为进程间的相互作用。细分为:直接相互作用和间接相互作用
相关进程 | 无关进程 | |
直接相互作用 | 发生 | 不发生 |
间接相互作用 | 发生 | 发生 |
进程之间会竞争使用共享资源,它们之间必须相互协调,彼此之间交换信息,这叫做进程之间的简单通信。
6.1.1 进程的同步
进程同步是指进程之间一种直接的协同关系,是一些进程相互合作,共同完成一项任务。进程间的直接相互作用构成进程的同步。
6.1.2 进程的互斥
(1)进程互斥:顾名思义,对于一些共享资源,一次只能为一个进程服务,自然就产生了互斥关系
(2)临接区:系统中一些资源一次只允许一个进程使用,这类资源称为临界资源或共享变量。
在进程中访问临界资源的那一段程序称为临界区。对于想进入临界区的进程构成了互斥关系。
6.1.3 同步机制
系统中设置解决进程同步的专门同步机制。已有的:硬件同步机制、信号量及PV操作、管程等。
进程同步机制——信号量和P、V操作
信号量semaphore:是一个特殊的变量,它的表面形式是一个整数变量附加一个队列。
struct semaphore
{
int count;
queueType queue;
}
二元信号量(解决互斥问题)
多元信号量(解决同步问题)
1)信号量的物理意义:
- S>0表示有S个资源可用
- S=0表示资源可用
- S<0则|S|表示的等待队列中的进程的个数
P(S):申请一个资源
V(S):释放一个资源
2)注意事项:
- 信号量的初值应该大于等于0
- pv操作必须成对出现
- 互斥操作时,同处一个进程;同步操作,不在同一进程出现
- 同步P操作在互斥P操作之前,两个V操作无关紧要
P、V操作:原语操作
P(s)
{
s.count--;
if(s.count<0)
{
进程状态设为阻塞状态;
并插入相应的等待队列s.queue末尾,
直到有其他进程在S上执行V操作
}
}
V(s)
{
s.xount++;
if(s.count<=0)
{
唤醒相应等待队列s.queue中等待的一个进程;
令其状态为就绪状态,并将其插入到就绪队列。
}
}
6.2 经典IPC问题
有了前面的理论基础,下面上几道经典题型,走过路过不要错过。
6.2.1读者写者问题
int rc=0;//读者数量
semaphore mutex=1;//控制对rc的访问(因为会有多个读者,读者数量的增减操作是要互斥的)
semaphore db=1;//控制对数据库的访问(读者在读数据库,写着就不能写数据库,反之亦然)
void reader(void)
{
while(true)
{
P(mutex);
rc=rc+1;
if(rc==1) P(db);//第一个读者,对数据库的操作关门
V(mutex);
read_data_base();//读数据库
P(mutex);
rc=rc-1;
if(rc==0)V(db);//最后一个读者,开门,释放对数据库的保护
V(mutex);
}
}
void writer(void)
{
while(true)
{
P(db);//测试一下,看看能不能对数据库操作
write_data_base();
V(db);
}
}
int rc=0;//读者的数量
semaphore mutex=1;//对rc互斥
semaphore db=1;//读者、写者对数据库的互斥
semaphore w=1;//标记谁先来
int main()
{
cobegin
reader();
weiter();
coend
}
void reader()
{
P(w);
P(mutex);
rc++;
if(rc==1) P(db);//关门
V(mutex);
V(w);
read_data_base();
P(mutex);
rc--;
if(rc==0)V(db);
V(mutex);
use_data_read();
}
void writer(void)
{ think_up_data();
P(w);
P(db);
write_data_base();
V(db);
V(w);
}
6.2.2哲学家就餐问题
#define N 5
#define LEFT (i+N-1)%N
#define RIGHT (i+1)%N
#define THINKING 0
#define EATING 1
#define HUNGRY 2
#typedef int semaphore;
int state[N]=THINKING;//状态
semaphore mutex=1;//临界区互斥
semaphore S[N]=0;//每个哲学家一个信号量
void test(int i)
{
if(state[i]==HUNGRY && state[LEFT] == THINKING && state[RIGHT] == THINKING)
{
state[i]=EATING;
V(S[i]);
}
}
void philosopher(int i)
{
思考;
P(mutex);
state[i]=HUNGRY;
test(i);
V(mutex);
P(S[i]);
拿起左筷子;
拿起右筷子;
用餐;
放下左筷子;
放下右筷子;
P(mutex);
state[i]=THINKING;
test(LEFT);
test(RIGHT);
V(mutex);
}
6.2.3爱睡觉的理发师问题
【问题解决】
int waiting=0;
semaphore mutex=1;//用于waiting 增减的互斥
semaphore chair=1;//每次理发椅只有一把,顾客互斥使用
semaphore ready=0;//同步,顾客通知理发师准备好了
semaphore finish=0;//同步,理发师通知顾客,可以来理发了
main()
{
cobegin
barber();
customer();
coend
}
barber()
{
while(true)
{
P(ready);
cut_hair();
V(finish);
}
}
customer()
{
P(mutex);
if(waiting<=N)
{waiting=waiting+1;
V(mutex);
P(chair);
V(ready);
P(finish);
V(chair);
P(mutex);
waiting=waiting-1;
V(mutex);
}
else{
V(mutex);exit;
}
}
6.2.4酒吧设备租赁问题
【问题描述】
在一间酒吧里有三个音乐爱好者,第一位音乐爱好者只有随身听,第二位只有音乐CD,第三位只有电池。而要听音乐就必须随身听、音乐CD和电池这三种物品俱全。酒吧老板一次出借这三种物品中的任意两种。当一名音乐爱好者得到这三种物品并听完一首乐曲后,酒吧老板才能再一次出借这三种物品中的任意两种。于是第二名音乐爱好者得到这三种物品,并开始听乐曲。整个过程就这样进行下去。
试用P、V操作正确完成这一过程。
【问题解决】
borrow1: CD+电池
borrow2:随身听+电池
borrow3:随身听+CD
semaphore over=0;
semaphore borrow1=0;
semaphore borrow2=0;
semaphore borrow3=0;
main()
{
cobegin
boss();
fans_i();(i=1,2,3)
coend
}
boss()
{
int s=guessservice();
if(s==1)V(borrow1);
elseif(s==2)V(borrow2);
else(s==3)V(borrow3);
P(over);
}
fans_i()
{
P(borrowi);
听音乐
V(over);
}
6.2.5巴拿马运河问题
【问题描述】
巴拿马运河建在太平洋和大西洋之间。由于太平洋和大西洋水面高度不同,有巨大落差,所以运河中修建有T(T>=2)级船闸,并且只能允许单向通行。船闸依次编号为1、2、…、T。由大西洋来的船需经由船闸 T、T-1、…、2,1通过运河到太平洋;由太平洋来的船需经由船闸1、2、…、T一1,T通过运河到大西洋。
试用P、V操作正确解决大西洋和太平洋的船只通航问题。
【问题解决】
int countPA[k]=0;//太平洋到大西洋 每个船闸的初船只数量
int countAP[k]=0;//大西洋到太平洋 每个船闸的初船只数量
semaphore mutex1=1;
semaphore mutex2=1;
semaphore s[k]=1;//每个船闸的互斥操作
main()
{
cobegin
P2A();
A2P();
coend
}
void P2A()
{
int i;
for(i=1;i<=T;i++)
{
P(mutex1);
countPA[i]++;
if(countPA[i]==1)//该船闸来的第一只船
P(S[i]);//关门
V(mutex1);
过第i个船闸;
P(mutex1);
countPA[i]--;
if(countPA[i]==0)V(S[i]);//最后一只船,开门
V(mutex1);
}
}
void A2P()
{
int i;
for(i=T;i>=1;i--)
{
P(mutex2);
countAP[i]++;
if(countAP[i]==1)//该船闸来的第一只船
P(S[i]);//关门
V(mutex2);
过第i个船闸;
P(mutex2);
countAP[i]--;
if(countAP[i]==0)V(S[i]);//最后一只船,开门
V(mutex2);
}
}
6.2.6银行叫号问题
【问题描述】
某银行有人民币储蓄业务,由n个柜员负责。每个顾客进入银行后先取一个号,并且等着叫号。当一个柜台人员空闲下来,就叫下一个号。试用P、V操作正确编写柜台人员和顾客进程的程序。
【问题解决】
semaphore mutex=1;//互斥
semaphore client=0;//客户数量
semaphore call=0;//同步
semaphore finish=0//同步
void main()
{
cobegin
client();
server_i();i=1,2,3...,n
coend
}
void client()
{
P(mutex);
取号;
V(mutex);
V(client);
P(call);//被叫号
被服务 ;
P(finish);//是否结束服务是由柜员传达
}
void server_i()
{
while(true)
{
P(client);
P(mutex);
取出队首号码;
V(mutex);
V(call);//叫号
服务;
V(finish); //服务结束 ,把信息传递给顾客
}
}
7.管程
7.1引入
PV操作比较复杂
7.2管程的定义和基本特征
管程是一种特殊的软件模块,由一下部分组成:
- 局部于管程的共享数据结构说明
- 对该数据结构进行操作的一组过程,即函数
- 对局部于管程的共享数据设置初始值的语句
- 管程有一个名字
基本特征:
- 局部于管程的数据只能被局部于管程的过程所访问
- 一个进程只有通过调用管程内的过程才能进入管程访问共享数据
- 每次仅允许一个进程在管程内执行某个内部过程