标签:
进程管理
程序的顺序执行:仅当前一操作(程序段)执行完后,才能执行后续操作。
程序顺序执行时的特征:顺序性,封闭性,可再见性。
前趋图(Precedence Graph)是一个有向无循环图,记为DAG(Directed Acycilc 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)
程序并发执行时的特征
间断性
失去封闭性
不可再现性
结构特征:
动态性
并发性
独立性
异步性
较典型的进程定义:
进程是程序的一次执行。
进程是一个程序及其数据在处理机上顺序执行时所发生的活动。
进程是出现在一个数据集合上运行的过程,它是系统进行资源分配和调度的一个独立单位。
就绪(Ready)状态
执行状态
阻塞状态
引起挂起状态的原因:
终端用户的请求;
父进程请求;
负责调节的需求;
操作系统的需要
进程状态的转换
活动就绪-->静止就绪
活动阻塞-->静止阻塞
静止就绪-->活动就绪
静止阻塞-->活动阻塞
具有挂起状态的进程状态图
进程控制块的作用:进程控制块的作用是使一个在多道程序环境下不能独立运行的程序(含数据),成为一个能独立运行的基本单位,一个能与其它进程并发执行的进程。
进程控制块中的信息
进程标识符: 进程标识符用于惟一地标识一个进程。一个进程通常有两种标识符:
内部标识符:在所有的操作系统中,都为每一个进程赋予一个惟一的数字标识符,它通常是一个进程的序号。 设置内部标识符主要是为了方便系统使用
外部标识符:它由创建者提供,通常是由字母、数字组成,往往是由用户(进程)在访问该进程时使用。为了描述进程的家族关系, 还应设置父进程标识及子进程标识。此外,还可设置用户标识,以指示拥有该进程的用户
处理机状态:处理机状态信息主要是由处理机的各种寄存器中的内容组成的。
进度调度信息:在PCB中还存放一些与进程调度和进程对换有关的信息,包括:
进程状态,指明进程的当前状态,作为进程调度和对换时的依据
进程优先级,用于描述进程使用处理机的优先级别的一个整数,优先级别高的进程应优先获得处理机
进程调度所需的其它信息,它们与所采用的进程调度算法有关。
事件,是指进程由执行状态转变为阻塞状态所等待发生的事件,即阻塞原因。
进程控制信息
程序和数据的地址,是指进程的程序和数据所在的内存或外存地(首)址,以便再调度到该进程执行时,能从PCB中找到其程序和数据
进程同步和通信机制,指实现进程同步和进程通信时必需的机制,如信息队列指针、信号量等,他们可能全部或部分地放在PCB中
资源清单,是一张列出了除CPU以外的、进程所需的全部资源及已经分配到该进程的资源的清单。
链接指针,它给出了本进程(PCB)所在队列中的下一个进程的PCB的首地址。
进程控制块的组织方式
PCB链表队列示意图:
正在执行的进程,当发现上述某事件时,由于无法继续执行,于是进程便通过调用阻塞原语block把自己阻塞。可见,进程的阻塞是进程自身的一种主动行为。进入block过程后,由于此时该进程还处于执行状态,所以应先立即停止执行,把进程控制块中的现行状态由“执行”改为阻塞,并将PCB插入阻塞队列。如果系统中设置了因不同事件而阻塞的多个阻塞队列,则应将本进程插入到具有相同事件的阻塞(等待)队列。 最后,转调度程序进行重新调度,将处理机分配给另一就绪进程,并进行切换,亦即,保留被阻塞进程的处理机状态(在PCB中),再按新进程的PCB中的处理机状态设置CPU的环境。
当被阻塞进程所期待的事件出现时,如I/O完成或其所期待的数据已经到达,则由有关进程(比如,用完并释放了该I/O设备的进程)调用唤醒原语wakeup( ),将等待该事件的进程唤醒。
原语执行的过程是:首先把被阻塞的进程从等待该事件的阻塞队列中移出,将其PCB中的现行状态由阻塞改为就绪,然后再将该PCB插入到就绪队列中
进程同步的主要任务是对多个相关进程在执行次序上进行协调,以使并发执行的诸进程之间能有效地共享资源和相互合作,从而使程序的执行具有可再现性。
临界资源(Critical Resouce) 许多硬件资源如打印机、磁带机等,都属于临界资源,诸进程间应采取互斥方式,实现对这种资源的共享。
生产者-消费者(producer-consumer)问题:有一群生产者进程在生产产品,并将这些产品提供给消费者进程去消费。为使生产者进程与消费者进程能并发执行,在两者之间设置了一个具有n个缓冲区的缓冲池,生产者进程将它所生产的产品放入一个缓冲区中; 消费者进程可从一个缓冲区中取走产品去消费。尽管所有的生产者进程和消费者进程都是以异步方式运行的,但它们之间必须保持同步,即不允许消费者进程到一个空缓冲区去取产品;也不允许生产者进程向一个已装满产品且尚未被取走的缓冲区中投放产品。
我们可利用一个数组来表示上述的具有n个(0,1,…,n-1)缓冲区的缓冲池。用输入指针in来指示下一个可投放产品的缓冲区,每当生产者进程生产并投放一个产品后,输入指针加1;用一个输出指针out来指示下一个可从中获取产品的缓冲区,每当消费者进程取走一个产品后,输出指针加1。 由于这里的缓冲池是组织成循环缓冲的,故应把输入指针加1表示成 in∶=(in+1)mod n;输出指针加1表示成out∶=(out+1) mod n。当(in+1) mod n=out时表示缓冲池满;而in=out则表示缓冲池空。此外,还引入了一个整型变量counter, 其初始值为0。每当生产者进程向缓冲池中投放一个产品后,使counter加1;反之,每当消费者进程从中取走一个产品时, 使counter减1。生产者和消费者两进程共享下面的变量:
Var n, integer;
type item=…;
var buffer:array[0, 1, …, n-1] of item;
in, out: 0, 1, …, n-1;
counter: 0, 1, …, n;
指针in和out初始化为1。在生产者和消费者进程的描述中,no-op是一条空操作指令,while condition do no-op语句表示重复的测试条件(condication),重复测试应进行到该条件变为false(假),即到该条件不成立时为止。在生产者进程中使用一局部变量nextp,用于暂时存放每次刚生产出来的产品;而在消费者进程中,则使用一个局部变量nextc,用于存放每次要消费的产品。 producer: repeat ... produce an item in nextp; ... while counter=n do no-op; buffer[in]:=nextp; in:=(in+1)mod n; counter: =counter+1; until false; consumer: repeat while counter=0 do no-op; nextc: =buffer[out]; out: =(out+1) mod n; counter: =counter-1; consumer the item in nextc; until false;
虽然上面的生产者程序和消费者程序,在分别看时都是正确的,而且两者在顺序执行时其结果也会是正确的,但若并发执行时,就会出现差错,问题就在于这两个进程共享变量counter。生产者对它做加1操作,消费者对它做减1操作,这两个操作在用机器语言实现时, 常可用下面的形式描述:
register 1:=counter; register 2:=counter;
register1:=register 1+1; register 2:=register 2-1;
counter:=register 1; counter: =register 2;
假设:counter的当前值是5。如果生产者进程先执行左列的三条机器语言语句,然后消费者进程再执行右列的三条语句, 则最后共享变量counter的值仍为5;反之,如果让消费者进程先执行右列的三条语句,然后再让 生产者进程执行左列的三条语句,counter值也还是5,但是,如果按下述顺序执行: register 1 :=counter; (register 1=5) register 1 :=register 1+1; (register 1=6) register 2 :=counter; (register 2=5) register 2 :=register 2-1; (register 2=4) counter :=register 1; (counter=6) counter :=register 2; (counter=4)
正确的counter值应该是5,但是现在是4.为了预防产生这种错误,解决此问题的关键是应把变量counter作为临界资源处理,即,令生产者进程和消费者进程互斥地访问变量counter。
同步机制应遵循的规则
最初由Dijkstra把整型信号量定义为一个用于表示资源数目的整型量S,除初始化外,仅能通过两个标准的原子操作(Atomic Operation) wait(S)和signal(S)来访问。 这两个操作被分别称为P、V操作。 wait和signal操作可描述为:
wait(S): while S≤0 do no-op;
S:=S-1;
signal(S):S:=S+1;
在信号量机制中,除了需要一个用于代表资源数目的整型变量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,表示只允许一个进程访问临界资源,此时的信号量转化为互斥信号量。
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;
一般“信号量集”的几种特殊情况: (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后,将阻止任何进程进入特定区。换言之,它相当于一个可控开关。
管程(Monitors):一种新的进程同步工具 1. 管程的定义 管程由四部分组成: - 管程的名称、 - 局部于管程内部的分享数据结构说明、 - 对该数据进行操作的一组过程、 - 对局部于管程内部的共享数据设置初始值的语句。
管程相当于围墙,它把共享变量和对它进行操作的若干过程围了起来,所有进程要访问临界资源时,都必须经过管程(相当于通过围墙的门)才能进入,而管程每次只准许一个进程进入管程,从而实现了进程互斥。 管程的特性:
2.条件变量 考虑一种情况:当一个进程调用了管程,在管程中时被阻塞或挂起,直到阻塞或挂起的原因解除,而在此期间,如果该进程不释放管程,则其他进程无法进入管程,被迫长时间地等待。为了解决这个问题,引入了条件变量condition。对这些条件变量的访问,只能在管程中进行。 管程中对每一个条件变量都需予以说明,其形式为:Var x,y:condition。对条件变量的操作仅仅是wait和signal,因此条件变量也是一种抽象数据类型,每个条件变量保存了一个链表,用来记录因该条件变量而阻塞的所有进程,同时提供的两个操作即可表示为x.wait和x.signal。其含义为
信号量机制作为同步工具是卓有成效的,但作为通信工具,则不够理想,主要表现在两个方面:
本节所介绍的是高级进程通信,是指用户可直接利用操作系统所提供的一组通信命令高效地传送大量数据的一种通信方式。操作系 统隐藏了进程通信的实现细节。即通信过程对用户是透明的,这样就大大减少了通信程序编制上的复杂性。
目前,高级通信机制可归结为三大类:共享存储器系统、消息传递系统以及管道通信系统。 1. 共享存储器系统 在共享存储器系统(Shared-Memory System)中,相互通信的进程共享某些数据结构或共享数据存储区,进程之间能够通过这些空间进行通信。 1. 基于共享数据结构的通信方式。 在这种通信方式中,要求诸进程公用某些数据结构,借以实现诸进程间的信息交换。这种通信方式是低效的,只适于传递相对少量的数据。 2. 基于共享存储区的通信方式 为了传输大量数据、在存储器中划出了一块共享存储区,诸进程可通过对共享存储区中数据的读或写来实现通信。进程在通信前,先向系统申请获得共享存储区中的一个分区,并指定该分区的关键字;若系统已经给其他进程分配了这样的分区,则将该分区的描述符返回给申请者,继之,由申请者把获得的共享存储区连接到本进程中;此后,便可像读、写普通存储器一样地读、写该公用存储分区。 2. 消息传递系统 消息传递系统(Message passing system)是当前应用最广泛的一种进程间的通信机制。在该机制中,进程间的数据交换是以格式化的消息(message)为单位的;在计算机网络中又把message称为报文。程序员直接利用操作系统提供的一组通信命令(原语),不仅能实现大量数据的传递,而且还隐藏了通信的实现细节,是通信过程对用户是透明的,从而大大减化了通信程序编制的复杂性。 3. 管道通信 所谓"管道",是指用于连接一个读进程和一个写进程以实现它们之间通信的一个共享文件,又名pipe文件。向管道(共享文件)提供输入
在OS中的每一个线程都可以利用线程标识符和一组状态参数进行描述。状态参数通常有这样几项:
线程在运行时,具有三种基本状态:
在多线程OS环境下,应用程序在启动时,通常仅有一个线程在执行,该线程被人们称为“初始化线程”。它可根据需要再去创建若干个线程。在创建新线程时,需要利用一个线程创建函数(或系统调用),并提供相应的参数,如指向线程主程序的入口指针、堆栈的大小,以及用于调度的优先级等。在线程创建函数执行完后,将返回一个线程标识符供以后使用。 终止线程的方式有两种:一种是在线程完成了自己的工作后自愿退出;另一种是线程在运行中出现错误或由于某种原因而被其它线程强行终止。
在多线程OS中,进程是作为拥有系统资源的基本单位,通常的进程都包含多个线程并为它们提供资源,但此时的进程就不再作为一个执行的实体。 多线程OS中的进程有以下属性: 1. 作为系统资源分配的单位 2. 可包括多个线程 3. 进程不是一个可执行的实体
内核支持线程 这里所谓的内核支持线程,也都同样是在内核的支持下运行的,即无论是用户进程中的线程,还是系统进程中的线程,他们的创建、撤消和切换等,也是依靠内核实现的。此外,在内核空间还为每一个内核支持线程设置了一个线程控制块, 内核是根据该控制块而感知某线程的存在的,并对其加以控制。
用户级线程 用户级线程仅存在于用户空间中。对于这种线程的创建、 撤消、线程之间的同步与通信等功能,都无须利用系统调用来实现。对于用户级线程的切换,通常是发生在一个应用进程的诸多线程之间,这时,也同样无须内核的支持。由于切换的规则远比进程调度和切换的规则简单,因而使线程的切换速度特别快。可见,这种线程是与内核无关的。
标签:
原文地址:http://www.cnblogs.com/six-moon/p/4930263.html