操作系统原理 第二章 进程管理(1)

2.1 进程的基本概念

2.1.1 进程的顺序执行及特征

1.程序的顺序执行

仅当前一操作(程序段)执行完后,才能执行后继操作。例如,在进行计算时,总须先输入用户的程序和数据,然后进行计算,最后才能打印计算结果。

2.程序顺序执行时的特征

  • 顺序性
  • 封闭性
  • 可再现性

2.1.2 前驱图

前趋图(Precedence Graph)是一个有向无循环图,记为DAG(Directed Acyclic Graph),用于描述进程之间执行的前后关系。图中的每个结点可用于描述一个程序段或进程,乃至一条语句;结点间的有向边则用于表示两个结点之间存在的偏序(Partial Order)前趋关系(Precedence Relation)“→”。
→={(Pi, Pj)|Pi must complete before Pj may start}, 如果(Pi, Pj)∈→,可写成Pi→Pj,称Pi是Pj的直接前趋,而称Pj是Pi的直接后继。在前趋图中,把没有前趋的结点称为初始结点(Initial Node),把没有后继的结点称为终止结点(Final Node)

每个结点还具有一个重量(Weight),用于表示该结点所含有的程序量或结点的执行时间。
在这里插入图片描述
图a具有以下前驱关系:

  P1→P2, P1→P3, P1→P4, P2→P5, P3→P5, P4→P6, P4→P7, P5→P8, P6→P8, P7→P9, P8→P9

还可以表示为:

P={P1, P2, P3, P4, P5, P6, P7, P8, P9}→={ (P1, P2), (P1, P3), (P1, P4),       
 (P2, P5), (P3, P5), (P4, P6), (P4, P7),(P5, P8), (P6, P8), (P7, P9), (P8, P9)} 

2.1.3 程序的并发执行及其特征

1.程序的并发执行
程序并发执行的前驱图
在该例中存在下述前趋关系:

  Ii→Ci,Ii→Ii+1, Ci→Pi, Ci→Ci+1,Pi→Pi+1

而Ii+1和Ci及Pi-1是重迭的,亦即在Pi-1和Ci以及Ii+1之间,可以并发执行。

2.程序并发执行时的特征

  • 间断性
  • 失去封闭性
  • 不可再现性
例如,有两个循环程序A和B,它们共享一个变量N。程序A每执行一次时,都要做N∶=N+1操作;程序B每执行一次时, 都要执行Print(N)操作,然后再将N置成“0”。程序A和B以不同的速度运行。 
      (1) N∶=N+1在Print(N)和N∶=0之前,此时得到的N值分别为n+1, n+1, 0。
      (2) N∶=N+1在Print(N)和N∶=0之后,此时得到的N值分别为n, 0, 1。
      (3) N∶=N+1在Print(N)和N∶=0之间,此时得到的N值分别为n, n+1, 0。 

2.1.4 进程的特征与状态

1.进程的特征和定义
进程特征:
1)结构特征
2)动态性
3)并发性
4)独立性
5)异步性

进程定义:
(1)进程是程序的一次执行
(2)进程是一个程序及其数据在处理机上顺序执行时所发生的活动
(3)进程是程序在一个数据集合上运行的过程,它是系统进行资源分配和调度的一个独立单元

在引入了进程实体的概念后,我们可以把传统OS中的进程定义为:“进程是进程实体的运行过程,是系统进行资源分配和调度的一个独立单位”

2.进程的三种基本状态
1)就绪(Ready)状态
2)执行状态
3)阻塞状态
在这里插入图片描述
3.挂起状态

1)引入挂起状态的原因

(1)终端用户的请求
(2)父进程请求
(3)负荷调节的需要
(4)操作系统的需要

2)进程状态的转换

(1)活动就绪→静止就绪。 
(2) 活动阻塞→静止阻塞。 
(3) 静止就绪→活动就绪。 
(4) 静止阻塞→活动阻塞。 

在这里插入图片描述

2.1.5 进程控制块(PCB)

为了描述控制进程的运行,系统中存放进程的管理和控制信息的数据结构称为进程控制块(PCB Process Control Block),它是进程实体的一部分,是操作系统中最重要的记录性数据结构。它是进程管理和控制的最重要的数据结构,每一个进程均有一个PCB,在创建进程时,建立PCB,伴随进程运行的全过程,直到进程撤消而撤消。

1.进程控制块的作用

进程控制块的作用是使一个在多道程序环境下不能独立运行的程序(含数据),成为一个能独立运行的基本单位,一个能与其它进程并发执行的进程。或者说,OS是根据PCB来对并发执行的进程进行控制和管理的。

2.进程控制块中的信息

1) 进程标识符
        进程标识符用于惟一地标识一个进程。一个进程通常有两种标识符:
       (1) 内部标识符。在所有的操作系统中,都为每一个进程赋予一个惟一的数字标识符,它通常是一个进程的序号。
        设置内部标识符主要是为了方便系统使用。
       (2) 外部标识符。它由创建者提供,通常是由字母、数字组成,往往是由用户(进程)在访问该进程时使用。
       为了描述进程的家族关系, 还应设置父进程标识及子进程标识。此外,还可设置用户标识,以指示拥有该进程的用户。
        
 2) 处理机状态
        处理机状态信息主要是由处理机的各种寄存器中的内容组成的。 
         ① 通用寄存器,又称为用户可视寄存器,它们是用户程序可以访问的,用于暂存信息, 在大多数处理机中,有 8~32 个通用寄存器,在RISC结构的计算机中可超过 100 个;
         ② 指令计数器,其中存放了要访问的下一条指令的地址;
         ③ 程序状态字PSW,其中含有状态信息,如条件码、执行方式、 中断屏蔽标志等;
          ④ 用户栈指针, 指每个用户进程都有一个或若干个与之相关的系统栈,用于存放过程和系统调用参数及调用地址。栈指针指向该栈的栈顶。 

3) 进程调度信息
       在PCB中还存放一些与进程调度和进程对换有关的信息,包括:
       ① 进程状态,指明进程的当前状态, 作为进程调度和对换时的依据;
       ② 进程优先级,用于描述进程使用处理机的优先级别的一个整数, 优先级高的进程应优先获得处理机;
        ③ 进程调度所需的其它信息,它们与所采用的进程调度算法有关,比如,进程已等待CPU的时间总和、 进程已执行的时间总和等;
        ④ 事件,是指进程由执行状态转变为阻塞状态所等待发生的事件,即阻塞原因。 

4) 进程控制信息
        进程控制信息包括:
        ① 程序和数据的地址, 是指进程的程序和数据所在的内存或外存地(首)址,以便再调度到该进程执行时,能从PCB中找到其程序和数据;
        ② 进程同步和通信机制,指实现进程同步和进程通信时必需的机制, 如消息队列指针、信号量等,它们可能全部或部分地放在PCB中; 
        ③ 资源清单,是一张列出了除CPU以外的、进程所需的全部资源及已经分配到该进程的资源的清单;
        ④ 链接指针, 它给出了本进程(PCB)所在队列中的下一个进程的PCB的首地址。 

3.进程控制块的组织方式
1)链接方式
(PCB链接队列示意图 )
PCB链接队列示意图
2)索引方式
(按索引方式组织PCB )
在这里插入图片描述


2.2 进程控制

2.2.1 进程的创建

1.进程图(Process Graph)
(进程树)
进程树

2.引起进程创建的事件

  • 用户登录
  • 作业调度
  • 提供服务
  • 应用请求

3.进程的创建
(1)申请空白PCB。
(2) 为新进程分配资源。
(3) 初始化进程控制块。
(4) 将新进程插入就绪队列,如果进程就绪队列能够接纳新进程, 便将新进程插入就绪队列。

2.2.2 进程的终止

1. 引起进程终止(Termination of Process)的事件

  1. 正常结束
    在任何计算机系统中,都应有一个用于表示进程已经运行完成的指示。例如,在批处理系统中,通常在程序的最后安排一条Holt指令或终止的系统调用。当程序运行到Holt指令时,将产生一个中断,去通知OS本进程已经完成。在分时系统中,用户可利用Logs off去表示进程运行完毕, 此时同样可产生一个中断,去通知OS进程已运行完毕。

  2. 异常结束
    在进程运行期间,由于出现某些错误和故障而迫使进程终止。这类异常事件很多,常见的有:
    越界错误。这是指程序所访问的存储区,已越出该进程的区域;
    保护错。进程试图去访问一个不允许访问的资源或文件,或者以不适当的方式进行访问,例如,进程试图去写一个只读文件;
    非法指令。程序试图去执行一条不存在的指令。出现该错误的原因,可能是程序错误地转移到数据区,把数据当成了指令;
    特权指令错。用户进程试图去执行一条只允许OS执行的指令;
    运行超时。进程的执行时间超过了指定的最大值;
    等待超时。进程等待某事件的时间, 超过了规定的最大值;
    算术运算错。进程试图去执行一个被禁止的运算,例如,被0除;
    I/O故障。这是指在I/O过程中发生了错误等。

  3. 外界干预
    外界干预并非指在本进程运行中出现了异常事件,而是指进程应外界的请求而终止运行。这些干预有:
    操作员或操作系统干预。 由于某种原因,例如,发生了死锁, 由操作员或操作系统终止该进程;
    父进程请求。 由于父进程具有终止自己的任何子孙进程的权利, 因而当父进程提出请求时,系统将终止该进程;
    父进程终止。 当父进程终止时,OS也将他的所有子孙进程终止。

2.进程的终止过程

(1) 根据被终止进程的标识符,从PCB集合中检索出该进程的PCB,从中读出该进程的状态。
(2) 若被终止进程正处于执行状态,应立即终止该进程的执行,并置调度标志为真,用于指示该进程被终止后应重新进行调度。
(3) 若该进程还有子孙进程,还应将其所有子孙进程予以终止,以防他们成为不可控的进程。
(4) 将被终止进程所拥有的全部资源,或者归还给其父进程, 或者归还给系统。
(5) 将被终止进程(它的PCB)从所在队列(或链表)中移出, 等待其他程序来搜集信息。

2.2.3 进程的阻塞与唤醒

1.引起进程阻塞与唤醒的事件:

  • 请求系统服务
  • 启动某种操作
  • 新数据尚未到达
  • 无新工作可做

2.进程阻塞过程
正在执行的进程,当发现上述某事件时,由于无法继续执行,于是进程便通过调用阻塞原语block把自己阻塞。可见,进程的阻塞是进程自身的一种主动行为。进入block过程后,由于此时该进程还处于执行状态,所以应先立即停止执行,把进程控制块中的现行状态由“执行”改为阻塞,并将PCB插入阻塞队列。如果系统中设置了因不同事件而阻塞的多个阻塞队列,则应将本进程插入到具有相同事件的阻塞(等待)队列。 最后,转调度程序进行重新调度,将处理机分配给另一就绪进程,并进行切换,亦即,保留被阻塞进程的处理机状态(在PCB中),再按新进程的PCB中的处理机状态设置CPU的环境。

3.进程唤醒过程
当被阻塞进程所期待的事件出现时(不再阻塞),如I/O完成或其所期待的数据已经到达,则由有关进程(比如,用完并释放了该I/O设备的进程)调用唤醒原语wakeup( ),将等待该事件的进程唤醒。唤醒原语执行的过程是:首先把被阻塞的进程从等待该事件的阻塞队列中移出,将其PCB中的现行状态由阻塞改为就绪,然后再将该PCB插入到就绪队列中 。

2.2.4 进程的挂起与激活

1.进程的挂起
当出现了引起进程挂起的事件时,比如,用户进程请求将自己挂起,或父进程请求将自己的某个子进程挂起, 系统将利用挂起原语**suspend( )**将指定进程或处于阻塞状态的进程挂起。挂起原语的执行过程是:首先检查被挂起进程的状态,若处于活动就绪状态,便将其改为静止就绪;对于活动阻塞状态的进程,则将之改为静止阻塞。为了方便用户或父进程考查该进程的运行情况而把该进程的PCB复制到某指定的内存区域。最后,若被挂起的进程正在执行,则转向调度程序重新调度。

2.进程的激活过程
当发生激活进程的事件时,例如,父进程或用户进程请求激活指定进程,若该进程驻留在外存而内存中已有足够的空间时,则可将在外存上处于静止就绪状态的进程换入内存。这时,系统将利用激活原语 active( ) 将指定进程激活。 激活原语先将进程从外存调入内存,检查该进程的现行状态,若是静止就绪,便将之改为活动就绪;若为静止阻塞便将之改为活动阻塞。假如采用的是抢占调度策略,则每当有新进程进入就绪队列时,应检查是否要进行重新调度,即由调度程序将被激活进程与当前进程进行优先级的比较,如果被激活进程的优先级更低,就不必重新调度;否则,立即剥夺当前进程的运行,把处理机分配给刚被激活的进程。


2.3 进程同步

2.3.1 进程同步的基本概念

1.两种形式的制约关系

  • 间接相互制约
  • 直接相互制约

2.临界资源

生产者-消费者(producer-consumer)问题是一个著名的进程同步问题。它描述的是:有一群生产者进程在生产产品,并将这些产品提供给消费者进程去消费。为使生产者进程与消费者进程能并发执行,在两者之间设置了一个具有n个缓冲区的缓冲池,生产者进程将它所生产的产品放入一个缓冲区中; 消费者进程可从一个缓冲区中取走产品去消费。尽管所有的生产者进程和消费者进程都是以异步方式运行的,但它们之间必须保持同步,即不允许消费者进程到一个空缓冲区去取产品;也不允许生产者进程向一个已装满产品且尚未被取走的缓冲区中投放产品。

**3. 临界区(critical section) **

在这里插入图片描述
4.同步机制应遵循的规则

  • 空闲让进
  • 忙则等待
  • 有限等待
  • 让权等待

2.3.2 信号量机制

1.整型信号量
最初由Dijkstra把整型信号量定义为一个整型量,除初始化外,仅能通过两个标准的原子操作(Atomic Operation) wait(S)和signal(S)来访问。这两个操作一直被分别称为P、V操作(P表示减一,V表示加一)。 wait和signal操作可描述为:

  wait(S): while S≤0 do no-op
                      S∶=S-1;
    signal(S):         S∶=S+1; 

2.记录型信号量
在整型信号量机制中的wait操作,只要是信号量S≤0, 就会不断地测试。因此,该机制并未遵循“让权等待”的准则, 而是使进程处于“忙等”的状态。记录型信号量机制,则是一种不存在“忙等”现象的进程同步机制。但在采取了“让权等待”的策略后,又会出现多个进程等待访问同一临界资源的情况。为此,在信号量机制中,除了需要一个用于代表资源数目的整型变量value外,还应增加一个进程链表L,用于链接上述的所有等待进程。记录型信号量是由于它采用了记录型的数据结构而得名的。它所包含的上述两个数据项可描述为:

type semaphore=record
         value:integer;
         L:list of process;
         end
相应地,wait(S)signal(S)操作可描述为:
procedure wait(S)
     var S: semaphore;
     begin
       S.value ∶=S.value-1;
       if S.value<0 then block(S,L)
     end
  procedure signal(S)
     var S: semaphore;
     begin
      S.value ∶ =S.value+1;
      if S.value≤0 then wakeup(S,L);
     end

在记录型信号量机制中,S.value的初值表示系统中某类资源的数目,
因而又称为资源信号量,对它的每次wait操作,意味着进程请求一个单位的该类资源,因此描述为S.value∶ =S.value-1;
当S.value<0时,表示该类资源已分配完毕,因此进程应调用block原语,进行自我阻塞,放弃处理机,并插入到信号量链表S.L中。可见,该机制遵循了“让权等待”准则。
此时S.value的绝对值表示在该信号量链表中已阻塞进程的数目。
对信号量的每次signal操作,表示执行进程释放一个单位资源,故S.value∶ =S.value+1操作表示资源数目加1。
若加1后仍是S.value≤0,则表示在该信号量链表中,仍有等待该资源的进程被阻塞,故还应调用wakeup原语,将S.L链表中的第一个等待进程唤醒。如果S.value的初值为1,表示只允许一个进程访问临界资源,此时的信号量转化为互斥信号量。

3.AND型信号量
在这里插入图片描述
AND同步机制的基本思想是:

将进程在整个运行过程中需要的所有资源,一次性全部地分配给进程,待进程使用完后再一起释放。
只要尚有一个资源未能分配给进程,其它所有可能为之分配的资源,也不分配给他。
亦即,对若干个临界资源的分配,采取原子操作方式:要么全部分配到进程,要么一个也不分配。
 由死锁理论可知,这样就可避免上述死锁情况的发生。
 为此,在wait操作中,增加了一个“AND”条件,故称为AND同步,或称为同时wait操作,
  即Swait(Simultaneous wait)定义如下: 
  Swait(S1, S2, …, Sn)
    if Si≥1 and … and Sn≥1 then
        for i∶ =1 to n do
        Si∶=Si-1;
        endfor
    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
    endif
Ssignal(S1, S2, …, Sn)
      for i∶   =1 to n do
      Si=Si+1;
      Remove all the process waiting in the queue associated with Si into the ready queue.
  endfor; 

4.信号量集
一般“信号量集”的几种特殊情况:
(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后,将阻止任何进程进入特定区。换言之,它相当于一个可控开关。

2.3.3 信号量的应用

1.利用信号量实现进程互斥

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 

2.利用信号量实现前驱关系
在这里插入图片描述

2.4 经典进程的同步问题

2.4.1 生产者-消费者问题

前面我们已经对生产者—消费者问题(The proceducer-consumer problem)做了一些描述,但未考虑进程的互斥与同步问题,因而造成了数据Counter的不定性。由于生产者—消费者问题是相互合作的进程关系的一种抽象,例如, 在输入时,输入进程是生产者,计算进程是消费者;而在输出时,则计算进程是生产者,而打印进程是消费者, 因此,该问题有很大的代表性及实用价值。

1. 利用记录型信号量解决生产者—消费者问题
假定在生产者和消费者之间的公用缓冲池中,具有n个缓冲区,这时可利用互斥信号量mutex实现诸进程对缓冲池的互斥使用;利用信号量empty和full分别表示缓冲池中空缓冲区和满缓冲区的数量。又假定这些生产者和消费者相互等效,只要缓冲池未满,生产者便可将消息送入缓冲池;只要缓冲池未空,消费者便可从缓冲池中取走一个消息。对生产者—消费者问题可描述如下:

Var mutex, empty, full:semaphore∶=1,n,0;
    buffer:array[0,, n-1] of item;
    in, out: integer∶=0, 0;
    begin
     parbegin
      proceducer:begin
            repeat
             …
            producer an item nextp;wait(empty);
            wait(mutex);
            buffer(in)=nextp;
            in=(in+1) mod n;
            signal(mutex);
            signal(full);
            until false;
          end
consumer:begin
           repeat
            wait(full);
            wait(mutex);
            nextc∶   =buffer(out);
            out∶   =(out+1) mod n;
            signal(mutex);
            signal(empty);
            consumer the item in nextc;
           until false;
          end
    parend
  end 

在生产者—消费者问题中应注意:首先,在每个程序中用于实现互斥的**wait(mutex)signal(mutex)**必须成对地出现; 其次,对资源信号量empty和full的wait和signal操作,同样需要成对地出现,但它们分别处于不同的程序中。例如,wait(empty)在计算进程中,而signal(empty)则在打印进程中,计算进程若因执行wait(empty)而阻塞, 则以后将由打印进程将它唤醒;最后,在每个程序中的多个wait操作顺序不能颠倒。应先执行对资源信号量的wait操作,然后再执行对互斥信号量的wait操作,否则可能引起进程死锁。

2. 利用AND信号量解决生产者—消费者问题

ar mutex, empty, full:semaphore∶   =1, n, 0;
    buffer:array[0,, n-1] of item;
    in out:integer∶   =0, 0;
   begin
    parbegin
      producer:begin
            repeat
             …
            produce an item in nextp;Swait(empty, mutex);
            buffer(in)=nextp;
            in=(in+1)mod n;
            Ssignal(mutex, full);
           until false;
         end
consumer:begin
            repeat
             Swait(full, mutex);
             nextc∶   =buffer(out);
             out∶   =(out+1) mod n;
             Ssignal(mutex, empty);
             consumer the item in nextc;
            until false;
         end
      parend
    end 
发布了107 篇原创文章 · 获赞 68 · 访问量 7775

猜你喜欢

转载自blog.csdn.net/weixin_43092232/article/details/104945211