《计算机操作系统》第四版,逢考必过!!!
前趋图(Precedence Graph)是一个有向无循环图,记为DAG(Directed Acyclic Graph),用于描述进程之间执行的前后关系。
→={(Pi, Pj)|Pi must complete before Pj may start}, 如果(Pi, Pj)∈→,可写成Pi→Pj,称Pi是Pj的直接前趋,而称Pj是Pi的直接后继。把没有前趋的结点称为初始结点(Initial Node),把没有后继的结点称为终止结点(Final Node)。每个结点还具有一个重量(Weight),用于表示该结点所含有的程序量或结点的执行时间。
(1) 顺序性
(2) 封闭性
(3) 可再现性
只有不存在前驱关系的查询之间才可能并发执行
为了使参与并发执行的每个程序(含数据)能够独立运行,在操作系统中必须为之配置一个专门的数结构,成为进程控制块(PCB)。系统利用PCB来描述进程的基本情况和活动过程,进而控制和管理进程。由程序段、相关数据段和PCB三部分便构成了进程实体(进程映像),简称为进程。
创建进程即创建PCB,撤销进程即撤销PCB
进程是进程实体的运行过程,是系统进行资源分配和调度的一个独立单位
创建⇒静止就绪:考虑到系统当前资源状况和性能的要求,不分配给新建进程所需的资源,主要是主存,相应的系统将进程状态转为静止就绪状态,被放在外存,不参与调度。
计算机系统中,对每一个资源和每个进程都设置了一个数据结构,用于表征其实体,称为资源信息表或进程信息表,其中包含了资源或进程的标识、描述、状态等信息及一批指针。将数据结构分为四类:内存表、设备表、文件表、进程表。
PCB记录操作系统所需的,用于描述进程的当前情况以及管理进程运行的全部信息,是操作系统中最重要的记录型数据结构。使一个在多道程序环境下不能独立运行的程序(含数据),成为一个能独立运行的基本单位,一个能与其它进程并发执行的进程。或者说,OS是根据PCB来对并发执行的进程进行控制和管理的。
作用:
1)作为独立运行基本单位的标志
系统通过PCB感知进程的存在;PCB已成为进程存在于系统的唯一标志
2)能实现间断性运行方式
系统将CPU现场信息保存在被中断进程的PCB中,供该进程再次调度执行时恢复CPU现场时使用
传统意义上的静态程序,不具有保护或保存自己运行现场的手段,无法保证其运行结果的可再现
3)提供进程管理所需要的信息
4)提供进程调度所需要的信息
5)实现与其他进程的同步与通信
采用信号量机制,要求在每个进程都设置有相应的用于同步的信号量。
进程标识符:唯一标识一个进程(外部标识符、内部标识符)
处理机状态:由处理机的各种存储器中的内容组成
① 通用寄存器,又称为用户可视寄存器,它们是用户程序可以访问的,用于暂存信息, 在大多数处理机中,有 8~32 个通用寄存器,在RISC结构的计算机中可超过 100 个;
② 指令计数器,其中存放了要访问的下一条指令的地址;
③ 程序状态字PSW,其中含有状态信息,如条件码、执行方式、 中断屏蔽标志等;
④ 用户栈指针, 指每个用户进程都有一个或若干个与之相关的系统栈,用于存放过程和系统调用参数及调用地址。
进程调度信息
① 进程状态,指明进程的当前状态, 作为进程调度和对换时的依据;
② 进程优先级,用于描述进程使用处理机的优先级别的一个整数, 优先级高的进程应优先获得处理机;
③ 进程调度所需的其它信息,它们与所采用的进程调度算法有关,比如,进程已等待CPU的时间总和、 进程已执行的时间总和等;
④ 事件,是指进程由执行状态转变为阻塞状态所等待发生的事件,即阻塞原因。
进程控制信息
① 程序和数据的地址, 是指进程的程序和数据所在的内存或外存地(首)址,以便再调度到该进程执行时,能从PCB中找到其程序和数据;
② 进程同步和通信机制,指实现进程同步和进程通信时必需的机制, 如消息队列指针、信号量等,它们可能全部或部分地放在PCB中;
③ 资源清单,是一张列出了除CPU以外的、进程所需的全部资源及已经分配到该进程的资源的清单;
④ 链接指针, 它给出了本进程(PCB)所在队列中的下一个进程的PCB的首地址。
将一些于硬件紧密相关的模块、各种常用设备的驱动程序以及运行频率较高的模块,都安排在紧靠硬件的软件层次中,它们常驻内存,被称为OS的内核。
处理机的执行状态分为
两大方面的功能
一个进程创建另一个进程,创建进程的进程为父进程。子进程可继承父进程拥有的所有资源。
Windows中不存在层次结构。如果一个进程创建另外的进程获得了一个句柄,其作用相当于一个令牌,可以用来控制被创建的进程。但是句柄可以被传递,获得了句柄的进程就有控制其他进程的权力。是获得句柄与否,控制与被控制的简单关系。
有向树
(1) 申请空白PCB。
(2) 为新进程分配资源。
(3) 初始化进程控制块。
(4) 将新进程插入就绪队列,如果进程就绪队列能够接纳新进程, 便将新进程插入就绪队列。
(1) 根据被终止进程的标识符,从PCB集合中检索出该进程的PCB,从中读出该进程的状态。
(2) 若被终止进程正处于执行状态,应立即终止该进程的执行,并置调度标志为真,用于指示该进程被终止后应重新进行调度。
(3) 若该进程还有子孙进程,还应将其所有子孙进程予以终止,以防他们成为不可控的进程。
(4) 将被终止进程所拥有的全部资源,或者归还给其父进程, 或者归还给系统。
(5) 将被终止进程(它的PCB)从所在队列(或链表)中移出, 等待其他程序来搜集信息。
正在执行的进程,当发现上述某事件时,由于无法继续执行,于是进程便通过调用阻塞原语block把自己阻塞。进程的阻塞是进程自身的一种主动行为。进入block过程后,由于此时该进程还处于执行状态,所以应先立即停止执行,把进程控制块中的现行状态由“执行”改为阻塞,并将PCB插入阻塞队列。如果系统中设置了因不同事件而阻塞的多个阻塞队列,则应将本进程插入到具有相同事件的阻塞(等待)队列。 最后,转调度程序进行重新调度,将处理机分配给另一就绪进程,并进行切换,亦即,保留被阻塞进程的处理机状态(在PCB中),再按新进程的PCB中的处理机状态设置CPU的环境。
当被阻塞进程所期待的事件出现时,如I/O完成或其所期待的数据已经到达,则由有关进程(比如,用完并释放了该I/O设备的进程)调用唤醒原语wakeup( ),将等待该事件的进程唤醒。唤醒原语执行的过程是:首先把被阻塞的进程从等待该事件的阻塞队列中移出,将其PCB中的现行状态由阻塞改为就绪,然后再将该PCB插入到就绪队列中。
block和wakeup原语必须成对使用。
当出现了引起进程挂起的事件时,比如,用户进程请求将自己挂起,或父进程请求将自己的某个子进程挂起, 系统将利用挂起原语suspend( )将指定进程或处于阻塞状态的进程挂起。挂起原语的执行过程是:首先检查被挂起进程的状态,若处于活动就绪状态,便将其改为静止就绪;对于活动阻塞状态的进程,则将之改为静止阻塞。 为了方便用户或父进程考查该进程的运行情况而把该进程的PCB复制到某指定的内存区域。最后,若被挂起的进程正在执行,则转向调度程序重新调度。
当发生激活进程的事件时,例如,父进程或用户进程请求激活指定进程,若该进程驻留在外存而内存中已有足够的空间时,则可将在外存上处于静止就绪状态的进程换入内存。这时,系统将利用激活原语active( )将指定进程激活。 激活原语先将进程从外存调入内存,检查该进程的现行状态,若是静止就绪,便将之改为活动就绪;若为静止阻塞便将之改为活动阻塞。假如采用的是抢占调度策略,则每当有新进程进入就绪队列时,应检查是否要进行重新调度,即由调度程序将被激活进程与当前进程进行优先级的比较,如果被激活进程的优先级更低,就不必重新调度;否则,立即剥夺当前进程的运行,把处理机分配给刚被激活的进程。
临界资源或独占资源:把在一段时间内只允许一个进程访问的资源
生产者-消费者(producer-consumer)问题:所有的生产者进程和消费者进程都是以异步方式运行的,但它们之间必须保持同步,即不允许消费者进程到一个空缓冲区去取产品;也不允许生产者进程向一个已装满产品且尚未被取走的缓冲区中投放产品
把每个进程中访问临界资源的那段代码成为临界区。
(1) 空闲让进。
(2) 忙则等待。
(3) 有限等待。
(4) 让权等待。
可将标志看成一个锁,锁开进入,锁关等待。
为防止多个进程同时测试到锁为打开的状态,测试和关锁操作必须是连续的,不能分开进行。
在进入所测试之前关闭中断,直到完成锁测试并上锁之后才能打开中断。
"测试并建立"指令,是一条原语。
// lock=false资源空闲;lock=true资源被使用
Boolean TS(boolean *lock){
Boolean old;
Old = *lock;
*lock = true;
Return old;
}
Do{
…
while TS(&lock); //test until success
Critical section;
lock = false;
Remainder section;
}while(1);
交换两个字的内容
Void swap(boolean *a,boolean *b)
{
Boolean temp;
Temp=*a;
*a=*b;
*b=temp;
}
//为每个临界资源设置一个全局布尔变量lock,其初值为false;在每一个进程中再用一个局部布尔变量key。
Do{
Key=ture;
do {
swap(&lock,&key); //test until success
}while(key!=false);
Critical section;
lock=false;
…
}while(1);
利用硬件进行进程同步不符合“让权等待”原则,且难以解决复杂的进程同步问题
把整型信号量定义为用于表示资源数目的整型量S。除初始化外,仅能通过两个标准的原子操作(Atomic Operation) wait(S)和signal(S)来访问。这两个操作一直被分别称为P、V操作。
wait(S){
while(S<=0); /*do no-op*/
S--;
}
signal(S){
S++;
}
除了用于代表资源数目的整型变量value外,还增加一个进程链表指针list。
遵循“让权等待”准则
typedef struct{
int value; //资源信号量
struct process_control_block *list; //信号量链表
}semaphore;
wait(semaphore *S){
// 每次wait操作,意味着进程请求一个单位的该类资源,使系统中可供分配的该类资源少一个
S->value--;
// 该类资源已分配完毕,进程调用block原语自我阻塞,放弃处理机,并插入到信号量链表中
if(S->value<0) block(S->list);
}
//value的绝对值表示在该信号量链表中已阻塞进程的数目
signal(semaphore *S){
//每次signal操作不是进程释放一个单位资源,是系统中可分配的该类资源增加一个
S->value++;
//在该信号量链表中仍有等待该资源的进程被堵塞,应调用wakeup将list链表的第一个等待进程唤醒
if(S->value<=0) wakeup(S->list);
}
// 如果S.value的初值为1,表示只允许一个进程访问临界资源,此时的信号量转化为互斥信号量
一个进程需要获得两个或更多的共享资源后才能执行任务
对若干个临界资源的分配采取原子操作方式,要把它所请求的资源全部分配到进程,要么一个也不分配。
Swait(S1,S2,...,S3){
while(TRUE){
if(Si>=1 && ... && Sn>=1){
for(i=1; i<=n; i++)Si--;
break;
}
else{
place the process in the waiting queue associated with the first Si found
with Si<1, and set the program count of this process to the beginning of
Swait operation
}
}
}
Ssignal(S1,S2,...,Sn){
while(TRUE){
for(i=1; i<=n; i++){
Si++;
Remove all the process waiting in the queue associated with Si into
the ready queue.
}
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xaFqjPgO-1622354196595)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/a8124f93-dee6-43dd-bf58-2e0aee4810b2/Untitled.png)]
Swait(S1, t1, d1, …, Sn, tn, dn)
if Si≥t1 and … and Sn≥tn then
for i∶=1 to n do
Si∶=Si-di;
endfor
else
Place the executing process in the waiting queue of the first Si with Si<ti
and set its program counter to the beginning of the Swait Operation.
endif
signal(S1, d1, …, Sn, dn)
for i∶=1 to n do
Si ∶=Si+di;
Remove all the process waiting in the queue associated with Si into
the ready queue endfor;
一般“信号量集”的几种特殊情况:
(1) Swait(S, d, d)。 此时在信号量集中只有一个信号量S, 但允许它每次申请d个资源,当现有资源数少于d时,不予分配。
(2) Swait(S, 1, 1)。 此时的信号量集已蜕化为一般的记录型信号量(S>1时)或互斥信号量(S=1时)。
(3) Swait(S, 1, 0)。这是一种很特殊且很有用的信号量操作。当S≥1时,允许多个进程进入某特定区;当S变为0后,将阻止任何进程进入特定区。换言之,它相当于一个可控开关。
Var mutex:semaphore∶ =1;
begin
parbegin
process 1: begin
repeat
wait(mutex);
critical section
signal(mutex);
remainder seetion
until false;
end
process 2: begin
repeat
wait(mutex);
critical section
signal(mutex);
remainder section
until false;
end
parend
代表共享资源的数据结构以及由对该共享数据结构实施操作的一组过程所组成的资源管理程序共同构成了一个操作系统的资源管理模块,即管程。
管程由三部分组成:
若进程在管程中被阻塞或挂起,其他进程无法进入管程被迫长时间等待。为解决问题,引入条件变量condition。管程中对每个条件变量都给予说明,形式为condition x,y;
x.wait:正在调用管程的进程因x条件需要被阻塞或挂起,调用x.wait将自己插入到x条件的等待队列上并释放管程,知道x条件变化。此时其他进程可以用该管程
x.signal:正在调用管程的进程发现x条件发生变化,则调用x.signal,重新启动一个因x条件而阻塞或挂起的进程。
假定在生产者和消费者之间的公用缓冲池中,具有n个缓冲区,这时可利用互斥信号量mutex实现诸进程对缓冲池的互斥使用;利用信号量empty和full分别表示缓冲池中空缓冲区和满缓冲区的数量。又假定这些生产者和消费者相互等效,只要缓冲池未满,生产者便可将消息送入缓冲池;只要缓冲池未空,消费者便可从缓冲池中取走一个消息。对生产者—消费者问题可描述如下:
为了实现对筷子的互斥使用,可以用一个信号量表示一只筷子,由这五个信号量构成信号量数组。其描述如下:Var chopstick: array[0, …, 4] of semaphore;
可采取以下几种解决方法:
(1) 至多只允许有四位哲学家同时去拿左边的筷子,最终能保证至少有一位哲学家能够进餐,并在用毕时能释放出他用过的两只筷子,从而使更多的哲学家能够进餐。
(2) 仅当哲学家的左、右两只筷子均可用时,才允许他拿起筷子进餐。
(3) 规定奇数号哲学家先拿他左边的筷子,然后再去拿右边的筷子;而偶数号哲学家则相反。按此规定,将是1、 2号哲学家竞争1号筷子;3、4号哲学家竞争3号筷子。即五位哲学家都先竞争奇数号筷子,获得后,再去竞争偶数号筷子,最后总会有一位哲学家能获得两只筷子而进餐
(1)基于共享数据结构的通信方式。
(2) 基于共享存储区的通信方式。
发送进程利用OS所提供的发送命令,直接把消息发送给目标进程。
系统提供下述两条通信命令(原语):
Send(Receiver, message); 发送一个消息给接收进程;
Receive(Sender, message); 接收Sender发来的消息;
在接收进程接收消息的原语中的源进程参数,是完成通信后的返回值,接收原语可表示为:
Receive (id, message);
Send(mailbox, message); 将一个消息发送到指定信箱;
Receive(mailbox, message); 从指定信箱中接收一个消息;
类型
为了使多个程序能够更好地并发执行,又尽量减少系统的开销,由此引入线程的概念:将进程的资源属性和调度属性分开,对拥有资源的基本单位不做频繁的切换,而只对调度和分派的单位进程(即线程)进行调度。线程—作为调度和分派的基本单位。
(1)调度的基本单位(线程切换时仅需保存和设置少量寄存器内存,切换代价远低于进程)
(2) 并发性(不同线程之间也可以并发执行)
(3) 拥有资源(线程本身无系统资源,仅有一点必不可少的能确保独立运行的资源;线程允许多个线程共享该进程所拥有的资源)
(4) 独立性(同一进程的不同线程之间的独立性要比不同进程之间的独立性低得多)
(5)系统开销(线程更低)
(6)支持多处理机系统
同进程的三个状态
同进程控制块。系统为每个线程配置了一个线程控制块TCB,将所有用于控制和管理线程的信息记录在TCB中。
(1) 进程是一个可拥有资源的基本单位,作为系统资源分配的单位。
(2) 可包括多个线程,多个线程可以并发执行。
(3) 进程不是一个可执行的实体。在多线程OS中,把线程作为独立运行的基本单位。
但一个线程切换到另一个线程时,需要从用户态转到核心态进行。因为用户进程的线程是指用户态运行,而线程调度和管理是在内核实现的。
调度以线程为单位进行
在用户空间实现,与内核无关。
调度以进程为单位进行。
多对一模型
一对一模型
多对多模型
类似进程
在用户空间实现,所有的用户级线程都具有相同的结构,他们都允许在一个中间系统上。
1)运行时系统
所谓“运行时系统”是用于管理和控制线程的函数(过程)的集合。用户级线程在切换时不需要转入核心态,而是由运行时系统中的线程切换函数来执行切换任务。运行时系统中的所有函数都驻留用户空间,并作为用户级线程与内核之间的接口。
2)内核控制线程
又称为轻型进程LWP,LWP通过系统调用访问内核。用户线程需要与内核通信时才借助LWP访问内核。将内核与用户级线程隔离。当一个系统用户级线程数量很大时,为了节省系统开销,把这些LWP做成一个缓冲池,即“线程池”。任一用户线程都可连接到线程池的任何一个LWP上。