进程管理git
程序的顺序执行:仅当前一操做(程序段)执行完后,才能执行后续操做。程序员
程序顺序执行时的特征:顺序性,封闭性,可再见性。github
前趋图(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. 进程不是一个可执行的实体
内核支持线程 这里所谓的内核支持线程,也都一样是在内核的支持下运行的,即不管是用户进程中的线程,仍是系统进程中的线程,他们的建立、撤消和切换等,也是依靠内核实现的。此外,在内核空间还为每个内核支持线程设置了一个线程控制块, 内核是根据该控制块而感知某线程的存在的,并对其加以控制。
用户级线程 用户级线程仅存在于用户空间中。对于这种线程的建立、 撤消、线程之间的同步与通讯等功能,都无须利用系统调用来实现。对于用户级线程的切换,一般是发生在一个应用进程的诸多线程之间,这时,也一样无须内核的支持。因为切换的规则远比进程调度和切换的规则简单,于是使线程的切换速度特别快。可见,这种线程是与内核无关的。