程序的执行
进程的定义
在传统操作系统中,进程是能独立运行的动态实体,是资源分配和调度的基本单位。它由一组机器指令、数据和堆栈等组成。
进程的特征
进程和程序间的关系
程序是人们编好的、用来完成特定任务的一组指令集合,它是静态的文本文件,可以永久保存;进程是程序在(内核定义的数据结构)上的一次顺序执行,它是动态的,是操作系统进行资源分配和调度的基本单位。
一个进程可包含一个或多个程序,一个程序也可以被多个进程执行。他俩是多对多的关系。
进程的组成
进程映像由:进程控制块(PCB)、执行的程序、执行时所需要的数据、执行时使用的工作区组成。
1)进程控制块(PCB)
PCB是操作系统用于描述进程的基本情况、进程的运行变化的数据结构,是进程存在的唯一标志,记录了进程的标识信息、现场信息和控制信息,常驻内存。
①标识信息:唯一标识一个进程。包含进程标识、用户标识、父进程标识。
②现场信息:保存当进程发生状态转换时处理机的现场信息,以便该进程重获处理机后再恢复现场,从而继续执行。现场信息包括CPU通用寄存器的内容、CPU状态寄存器的内容、栈指针等。
③控制信息:操作系统对进程进行调度时用到的信息。包括进程状态(运行就绪阻塞)、调度信息、数据结构信息、队列指针、位置信息、通信信息、特权信息、存储信息、资源占有使用信息。
2)程序
若一个程序能被多个进程同时共享执行,则这个程序段就是以纯码(即可再入码)形式编写的,在进程执行时不可修改。
3)数据
4)工作区
工作区是指参数传递和系统调用时的执行环境。进程在核心态运行的工作区称为核心栈,在用户态下运行的工作区称为用户栈。
PCB的组织方式
- 线性方式
- 链接方式
- 索引方式
进程的基本状态
运行态、就绪态、阻塞态。它们的转换关系如下:
运行→阻塞:程序出让CPU,等待系统分配资源或某些事件的发生,如,暂时不能访问某一资源,操作系统尚未完成服务,系统正初始化I/O设备,等待用户输入的信息等;
运行→就绪:进程分配的时间片已用完,或在中断机制下有等高优先级的进程进入系统;
阻塞→就绪:处于阻塞队列的进程,当等待的事件已发生或等待的资源可用时,就进入就绪队列竞争CPU;
就绪→运行:处于就绪队列的进程被调度程序选中,从而占用CPU。
进程的创建
进程的创建有两种,一种是在系统生成时建立起一些系统进程(如系统调度进程),一种是由创建原语创建的进程,这些进程是非常驻的系统进程和用户进程。后者的创建过程为:调用进程创建原语→为进程分配一个空白PCB→为进程映像分配地址空间→初始化PCB→将该进程插入就绪队列和家族队列。
所谓创建状态,就是若系统尚无足够内存存储新建的进程,那么进程的创建工作便不能完成,于是进程便处在了创建状态。当其获得了所必须的资源,并完成了PCB的初始化后,方可转到就绪态。
进程的终止
进程的终止分两类:一类是进程完成了既定操作,正常结束,操作系统收回该进程占用的资源;一类是进程在执行过程中出现了异常,无法继续执行,操作系统令其终止,并收回资源。
总之,进程终止的原因包括:正常结束;非法使用特权指令;内存空间不足;等待时间过长;地址越界;非法使用共享内存区;子进程被父进程终止;父进程被终止;算术错误;输入/输出失败;操作系统干预终止。
当系统中发生了要求终止进程的某事件后,OS便会调用进程终止原语,按下述步骤终止指定的进程:①根据被终止进程的标识符,从PCB集合中检索出该进程的PCB,并从该进程的PCB中读出该进程的状态;②若被终止进程正处于执行状态,则立即终止该进程的执行,并置调度标志为真,以指示该进程被终止后应重新进行调度;③若该进程还有子孙进程,则还应终止其所有子孙进程,以防止它们成为不可控的进程;④将被终止的进程所拥有的全部资源,或归还给其父进程,或归还给系统;⑤将被终止进程的PCB从所在队列(或链表)中移出,等待其他程序来搜集信息。
PS:所谓进程的创建与终止,其实就是PCB的创建与撤销。
进程控制——原语
进程控制的主要任务:创建进程、撤销进程、实现进程的状态转换、实现进程间的通信。操作系统内核通过原语,实现了进程控制的功能。
原语是指由机械指令构成的可完成特定功能的程序段,是机械指令的集合,执行时不可中断。(机械指令就是二进制代码,它由操作码和地址码构成)
内核中包含这些原语:进程控制原语、进程通信原语、资源管理原语等。
进程控制原语又包含:进程创建原语、进程撤销原语、进程挂起原语、进程激活原语、进程阻塞原语、进程唤醒原语等。
进程阻塞原语:进程自己调用把自己阻塞
进程唤醒原语:由别的进程唤醒被阻塞的进程,让其进入就绪队列
进程挂起原语:当出现诸如内存资源不足时,将进程由内存调入外存
进程激活原语:由别的进程激活被挂起的进程,把进程由外存调入内存
进程间的同步与互斥
同步是进程—进程间的约束,故称直接制约;互斥是进程—资源—进程之间的约束,故称间接制约。
临界资源也叫独占资源,是指在一段时间内只允许一个进程访问的或有形或无形的资源,有形资源例如一些硬件设备(打印机、磁带机、绘图仪),无形资源例如进程共享的变量、数据、队列或使用权限。
进程中涉及临界资源的程序段称为临界区。各进程对临界区的访问应该是互斥的,所以也叫互斥区。用于检查临界区是否正在被使用的程序段叫做进入区,将互斥区使用标志恢复,并释放临界区的程序段叫做退出区。
访问临界区的原则/同步机制应遵循的规则:空闲让进,忙则等待,有限等待,让权等待。
实现进程互斥的方法
1、软件方法(弊端凸显,不推荐)
Dekker算法、Peterson算法(仅满足忙则等待、空闲让进、有限等待)
2、硬件方法
在对临界区进行管理时,可以将标志看作一个锁,“锁开”进入,“锁关”等待,初始时锁是打开的。每个要进入临界区的进程,必须先对锁进行测试,锁未开,必须等待,直至锁被打开。当锁打开时,则应立即把其锁上,以阻止其他进程进入临界区。显然,为防止多个进程同时测试到“锁开”,测试和关锁操作必须是连续的,不允许分开进行。
1)关中断(最简单)
在进入锁测试之前,关闭中断,直到完成锁测试并上锁之后,才能打开中断。
2)利用 Test-and-Set 指令
其一般性描述如下:
bool TestAndSet (bool *lock){
bool old = *lock;
*lock = true;
return old;
}
这条指令可以被看作一个函数,其执行过程是不可分割的,即一条原语。当lock=FALSE时,表示该资源空闲;当lock=TRUE时,表示该资源正在被使用。
利用TS指令实现互斥的循环进程结构可描述为:
do {
…
while TS(&lock); /* do skip */
临界区;
lock : = FALSE;
剩余区;
} while(TRUE);
3)利用 swap 指令
其处理过程描述如下:
void swap(boolean *a, boolean *b){
boolean temp;
temp = *a;
*a = *b;
*b = temp;
}
为每个临界资源设置一个全局的布尔变量lock,其初值为FALSE,在每个进程中再设置一个局部的布尔变量key,使用swap指令与lock进行数值交换,以此来循环判断lock的取值。只有当key为FALSE时,进程才可以进行临界区操作。利用swap指令实现进程互斥的循环过程描述如下:
do {
key=TRUE ;//默认不可操作临界区
do {
swap(&lock,&key);// key为FALSE时,内循环结束
} while (key!=FALSE);
临界区操作 ;
lock =FALSE ;
…
} while (TRUE) ;
当临界资源忙时存在“忙等”状态,不符合“让权等待”原则;很难解决复杂同步问题。
进程同步——信号量及PV操作
信号量S表示资源实体,是一个变量,除了定义初始化外,其值只能由PV操作改变。信号量分整形信号量、记录型信号量和AND 型信号量。
PV操作是原子操作,且是成对出现的。互斥时,它们处在同一进程中;同步时,它们处在不同的进程里。
1、整形信号量
P操作的原语如下:
P(S):
{ S--; //假设自减前S=0
if(S<0)
调用该P操作的进程阻塞,并插入阻塞队列;
}
执行P操作时,先申请资源,S自减1。若减1后的值为非负,则表示还有这么多个资源可用;为负,其绝对值表示希望申请资源但没有得到而进入阻塞状态的进程数。
V操作的原语如下:
V(S):
{ S++; //假设自加前S=-1
if(S<=0)
从等待信号量S的阻塞队列中唤醒一个进程;
}
2、记录型信号量
记录型信号量的数据结构如下:
typedef struct {
int value;
struct process_control_block *list;
} semaphore;
相应的wait(s)和signal(s)操作描述如下:
s semaphore;
wait(s) {
s.Value = s.Value-1;
if s.Value <0 then block(s.L);
}
signal(s) {
s.Value = s.Value+1;
if s.Value <=0 then wakeup(s.L);
}
3、AND 型信号量
在某些场合下,一个进程要先获得两类或更多的共享资源方能执行任务。
AND同步机制的基本思想:全分配全释放(将进程在整个运行过程中所需要的所有临界资源一次性分配给进程,使用完后一起释放,只要一个资源没有分配到则其它可能分配到的资源也不分配,已经分配的全部释放)
AND原语对资源的分配是原子的,其操作定义如下:
swait(s1,s2,...sn) {//申请进程所需要的全部资源
if ( s1>=1 and s2>=1 and ... sn>=1 ){//倘若这些资源都有空余
for i=1 to n do
si=si-1;
}else将进程放入所有对应的si等待队列;
}
ssignal(s1,s2,...sn) {
for i=1 to n do
si:=si+1;
}
进程通信
由于进程间存在同步和互斥的关系,所以需要进程间的通信。依据信息的多少及效率的高低,通信分为低级通信与高级通信。低级通信传送信息量小,P、V操作、管程都属于低级通信。高级通信传送信息量大,共享内存模式、消息传递模式、共享文件模式(管道)、客户机服务器模式都属于高级通信。
1、共享内存模式
系统在内存中指定一个区域作为共享存储区,一组进程向该区域中写,另一组进程从该区域中读,从而实现两组进程间的信息交换。
2、消息传递模式
系统中有一定数量的消息缓冲区,把消息缓冲区作为进程通信的基本单位,借助系统提供的发送原语Send(A)和接受原语Receive(B),实现进程之间的通信。进程若要发送消息,需使用Send(A)原语把消息从发送区复制到消息缓冲区,并将消息挂在接收进程的消息队列末尾。如果该接受进程因等待消息而处于阻塞状态,则将其唤醒。接收进程再用接受原语Receive(B)从消息队列头取走一个消息放到自己的接收区。
消息传递的方式又分为直接通信方式和间接通信方式。①直接通信方式,是指发送进程利用OS所提供的发送原语,直接把消息发送给目标进程;②间接通信方式,是指发送进程和接收进程都通过共享中间实体(称为信箱)的方式进行消息的发送和接收,进而完成进程间的通信。
3、管道
管道是指用于连接一个读进程和一个写进程以实现它们之间通信的一个共享文件,在内核中用文件描述符标识。
管道通信基于文件系统,连接两个通信进程,以先进先出的方式实现消息的单向传送。
4、客户机服务器模式
其主要的实现方法分为3类:套接字、远程过程调用和远程方法调用。
1)套接字可分为以下两类:
① 基于文件型(通信进程都运行在同一主机的网路环境下)
② 基于网络型(通信进程运行在不同主机的网络环境下)
2)远程过程调用和远程方法调用
远程过程调用(RPC)是一个通信协议,该协议允许运行于一台主机(本地)系统上的进程调用另一台主机(远程)系统上的进程。如果涉及的软件采用面向对象编程,那么远程过程调用亦可称作远程方法调用。
线程
线程是可执行的实体单元,可代替进程,是处理机调度和分派的基本单位。
多线程
多线程是指操作系统支持在单个进程中执行多个线程的能力。
在多线程环境中,进程仍作为资源分配的基本单位,但不再是一个基本的可执行实体,而把线程作为独立运行(调度)的基本单位。
一个进程里可以有一个或多个线程,多个线程可并发执行。
线程的特征
线程的执行状态包括运行、就绪和等待;
当不处于执行状态时,要保存线程上下文环境;
一个执行栈;
进程中的所有线程共享所属进程内的主存和其他资源。
线程的状态
创建。进程创建时线程也就创建了。一个线程可以在这个进程内部再创建其他线程。同进程内的线程共享存储空间和资源。线程创建后会进入就绪队列。
阻塞。
解除阻塞。
终止。
线程控制块TCB
TCB中通常含有:
①线程标识符;
②一组寄存器(包括程序计数器、状态寄存器和通用寄存器等)的内容;
③线程执行状态;
④优先级;
⑤线程专有存储区,用于在线程切换时存放现场保护信息和与该线程相关的统计信息等;
⑥信号屏蔽,即对某些信号加以屏蔽;
⑦堆栈指针,线程在执行时,经常会进行过程调用,而过程调用时通常会出现多重嵌套的情况,如此需要把每次过程调用中所使用的局部变量以及返回地址保存起来。在TCB中,也须设置两个指向堆栈的指针:指向用户自己堆栈的指针和指向核心栈的指针。前者是指当线程运行在用户态时,使用用户自己的用户栈来保存局部变量和返回地址;后者是指当线程运行在内核态时,使用系统的核心栈来保存局部变量和返回地址。
线程的实现
线程的实现方式包括用户级线程,核心级线程,还有两种方式的组合。
线程与进程的比较
1)线程调度类似于进程调度,也是在就绪线程中择一占用处理机。线程使用的调度程序可以是用户程序,可以是系统程序。在就绪态中的进程中所有线程都不被执行,在运行态的进程中才有线程的执行。
2)线程同进程一样具有并发性。
3)同一进程中的线程共享进程的资源和状态,但不归线程所有。线程具有线程控制块(TCB),程序计数器,寄存器以及堆栈。而进程有PCB、用户地址空间、执行程序和数据,是多个线程的集合。
4)在传统操作系统中,进程是资源分配和调度的基本单位,并且独立运行。进程的一次调度要引起两对进程上下文的四次切换,开销较大。而在引入线程的OS中,已把线程作为调度和分派的基本单位。当进行线程切换时,仅须保存和设置少量寄存器的内容,开销较小。类似的,创建撤销进程的开销也比创建撤销线程的开销大。
5)在同一进程中的不同线程之间的独立性,比不同进程之间的独立性低得多。这是因为,为防止进程之间彼此干扰和破坏,每个进程都拥有独立的地址空间和其他资源,它们除了共享全局变量外,不允许自身以外的进程访问自己地址空间中的地址。但是同一进程中的不同线程为了提高并发性以及满足进程间的合作需求而创建的,它们可以共享进程的内存地址空间和资源。每个线程都可以访问它们所属进程地址空间中的所有地址,一个线程的堆栈可以被其他线程读/写,甚至完全清除。
6)对于单线程进程,不管有多少处理机,也只能运行在一个处理机上。但对于多线程进程,其可以将一个进程中的多个线程分配到多个处理机上运行。