黑铁时代
Programing is not only one kind of technology, but also one kind of art.

进程的定义

  1. 一个正在执行中的程序;

  2. 一个正在计算机上执行的程序实例;

  3. 能够分配给处理器并由处理器执行的实体;

  4. 一个具有以下特征的活动单元:一组指令序列的执行,一个当前状态和相关系统资源的集合;

  由一组元素(包括两个基本元素:程序代码和代码相关的数据集)组成的实体,如果处理器开始执行该代码,这个执行实体就称为进程。在进程执行的任意一个时刻,都可以唯一的被表示为以下元素:

  标识符:和进程相关的唯一标示,用以区分不同的进程;

  状态:执行态,阻塞态,就绪态等;

  优先级:相对于其他进程的优先级;

  程序计数器:程序中即被执行的下一条指令的地址;

  内存指针:包括程序代码和进程相关数据的指针,还有和其他进程共享内存块的指针;

  上下文数据:进程执行时处理器寄存器的数据;

  I/O状态信息:包括显示I/0请求,分配给进程的I/O设备,被进程使用的文件列表;

  审计信息:包括处理器的使用时间,时钟数总和等等;

  这些信息都存放在一个叫做进程控制块的数据结构中,该控制块由操作系统创建和管理。进程控制块包含了充分的信息,这样操作系统就可以中断一个进程的执行,当进程被中断时,操作系统会把程序计数器和处理器寄存器保存到进程控制块的相应位置,并且进程状态值(如阻塞态)也会改变。然后操作系统就可以将其他进程的状态置为运行态,把要执行的进程的程序计数器和进程上下文数据加载到处理器寄存器中,这样其他进程就可以开始执行了。因此,可以认为进程由程序代码相关数据以及进程控制块组成。

  

进程的创建

  当一个新的进程需要被添加到进程队列中时,操作系统需要建立用于管理该进程的数据结构,并在主存中为它分配地址空间。

  导致进程创建的原因:

  

进程的终止

  导致进程终止的原因:

  

  所以以上的行为都会给操作系统发送一个服务请求,以终止发出请求的进程。

五状态模型

  运行态:进程正在执行。如果计算机只有一个处理器,那么就最多一个进程处于此状态;

  就绪态:进程做好运行的准备,等待处理器执行的机会;

  阻塞态:进程在某些事件发生之前不能执行,如等待I/0操作的完成;

  新建态:刚创建完成的进程,并没有加入到执行列表中。通常是进程块已经创建,但是并没有加载到主存中;

  退出态:因为某种原因,进程的执行被终止。

  当进程处于新建态的时候,程序代码不在主存中,也不会为程序的数据分配空间。操作系统可能因为性能,主存空间限制,进程数量限制等原因,并不会运行该进程。

  当进程处于退出态的时候,进程并不再被执行,但是也没有被立即删除,与作业相关的表和其他信息临时被操作系统保留起来,这为辅助程序或支持程序提供了提取所需信息的时间。当这些信息被提取完成后,操作系统就不需要再保留和该进程相关的数据了,然后将该进程从系统中删除。

状态的转换

  

  空 -> 新建态:创建一个新的进程;

  新建态 -> 就绪态:操作系统可以再接受一个进程时,就将一个新建态的进程转换成就绪态。大多数操作系统都对活动进程数量有一定限制,防止因为活跃进程过多导致系统性能下降;

  就绪态 -> 运行态:调度器或分派器负责选择一个就绪态的进程执行;

  运行态 -> 退出态:正在运行的进程自然完成,或者被操作系统终止时;

  运行态 -> 就绪态:(1)最常见的原因是“非中断执行”的处理器最长时间段到达;

             (2)由于优先级原因,进程可能被抢占。假设进程A正在执行,此时优先级更高的进程B处于阻塞态。如果在进程A在执行过程中,进程B等待的时间发                生了,进程B就变为就绪态。因为进程B的优先级高于进程A,处理器就中断进程A的执行,转而执行进程B ;

           (3)进程自愿释放对处理器的控制;

  运行态 -> 阻塞态:如果进程请求的某个事件必须等待,它就进入阻塞态。例如:

            (1)进程请求一个系统的服务,系统无法立即进行服务;

            (2)进程请求一个无法立即得到的资源,如文件或虚存中的共享区域;

                (3)或者必须先进行某种初始化工作;

           (4)进程间的通信,一个进程必须等待另一个进程提供的输入;

  阻塞态 -> 就绪态:进程等待的事件发生了;

  就绪态 -> 退出态:父进程终止子进程的执行,或者父进程终止后,所有相关的子进程都会被终止;

  阻塞态 -> 退出态:同上;

就绪队列和阻塞队列

  所有就绪态的进程都被放在就绪队列中,当操作系统需要选择一个进程执行的时候,就会从就绪队列中选择一个进程执行。就绪队列可以设计成简单的先进先出的处理方式。当进程被处理器执行后,如果没有被终止,它要么被继续放在就绪队列中,要么放在阻塞队列中。当某个等待时间发生后,阻塞队列中的等待这个事件的进程被转到就绪队列中。

  当事件发生后,操作系统会扫描整个阻塞队列,搜索出那些等待该事件的进程。如果系统中阻塞队列中的进程数量很大,扫描整个队列,将会非常低效。一种解决方案是:为每个事件指定一个队列,当事件发生时,只需要将对应队列中的所有进程转到就绪队列即可。

  如果操作系统支持进程优先级方案,那么根据不同优先级指定单独的队列,也是非常高效的。

  

被挂起的进程

  在没有虚拟内存的系统中,所有运行的进程都必须完全载入主存中。由于处理器的速度远远快于I/O,所有即使对于多道程序设计的系统,内存中的所有进程都在等待I/O的情况也很常见,大多数时候处理器就会处于空闲。

  一种解决方案就是交换,即把主存中的某个进程的一部分或全部转移到磁盘中。当主存中没有就绪态的进程时(即操作系统没有可以执行的进程),操作系统便把被阻塞的进程换出到磁盘中的挂起队列,这是暂时保存从主存中换出的进程队列。然后操作系统取出挂起队列中的一个进程,或者接受一个新进程,将其载入主存执行。

  由于交换是一个磁盘I/O操作,因此也可能是问题更加恶化。但是磁盘I/O操作相对来说算是系统中最快的I/O,所以通常情况下,交换可以提高性能。

  为了适应操作系统的交换策略,我们为进程增加一个挂起态。当主存中的所有进程都处于阻塞态时,操作系统就就将其中一个进程置为挂起态,并把它换出到磁盘中。主存中释放的空间就可以换入另一个进程到主存中执行。因此按照以上推理,挂起态的进程都是处于阻塞的,这里带来的一个问题是:如果操作系统完成一个换出操作后,需要再次选择一个挂起的进程执行,而如果挂起的进程都处于阻塞态,那么换入挂起的进程也毫无意义,挂起的进程必须等到事件发生后才能执行。因此,为了区分所以这些不同的情况,需要增加2个状态:

  阻塞/挂起态:进程在辅存中并等待一个事件;

  就绪/挂起态:进程在辅存中,载入主存就可以执行;

  

  新的状态转换:

    阻塞态 -> 阻塞/挂起态:如果没有就绪进程,至少一个阻塞进程被换出,为未阻塞的进程让出空间。如果正在运行的进程,或就绪进程需要更多的存储空间,即使存在   就绪进程,操作系统也可能换出进程;

    阻塞/挂起态 -> 就绪/挂起态:等待的事件发生;

    就绪/挂起态 -> 就绪态:如果主存中没有就绪态,操作系统换入一个进程执行;

    就绪态 -> 就绪/挂起态:通常操作系统并不倾向于挂起就绪进程。出现这种转换的情况有:(1)如果主存需要足够的空间,只能挂起一个就绪进程;(2)某个高优先  级的阻塞进程很快就会就绪,操作系统可能会挂起一个低优先级的就绪进程,而不是一个高优先级的阻塞进程;

    新建态 -> 就绪/挂起态 或者 新建态 -> 就绪态:当创建一个新的进程时,它要么加入就绪队列,要么加入就绪/挂起队列。不论哪种操作,操作系统都必须建立一些表  以管理进程,并为进程分配地址空间。通过这个策略,主存中经常会没有足够的空间分配给新进程,因此使用新建态到就绪/挂起态的转换。另一方面,操作系统会尽可能迟   的创建进程以减少操作系统的开销,并在系统被阻塞态进程所阻碍时再执行进程创建任务;

    阻塞/挂起态 -> 阻塞态:这种情况比较少见,因为将一个阻塞的进程换入主存,没有意义。但是如果阻塞/挂起态的进程优先级比就绪/挂起队列中任何进程的优先级都要  高,并且操作系统认为阻塞的事件很快就会发生,这时候换入该进程是合理的;

    运行态 -> 就绪/挂起态:(1)进程的运行时间段满;(2)阻塞/挂起队列中,某个较高优先级的进程不在阻塞,就可能抢占正在运行的进程,将其转换为就绪/挂起   态,并释放一些主存空间;

    各种状态 -> 退出态:运行的进程自然终止,或者发生错误条件被迫终止;一个进程也可以被父进程终止,或者父进程终止。进程在任何状态都可以转换到退出态;

进程描述

  操作系统为了管理进程和资源,必须掌握每个进程和资源当前状态的信息。使用的方式是构造并维护所管理的每个实体的信息表。操作系统维护着四种不同类型的表:内存,I/O,文件和进程。

  

  内存表:用于跟踪主存和辅存(虚拟内存)。主存的某些部分为操作系统保留,剩余部分是进程可以使用的,保存在辅存中的进程使用某种类型的虚拟内存或简单的交换机制。

  包括以下信息:

    分配给进程的主存;

    分配给进程的辅存;

    主存块和虚拟内存块的任何保护属性,如那些进程可以访问某些共享内存区域;

    管理虚拟内存所需要的任何信息;

  I/O表:管理计算机中的I/O设备和通道。在任何时刻,一个I/O设备要么是可用,要么是已经分配给某个特定的进程。如果正在进行I/O操作,则操作系统需要知道I/O的操作状态和I/O传送的源和目标的主存单元。

  文件表:文件是否存在,文件在辅存中的位置,当前状态和其他属性的信息。文件表的的信息可能有文件管理系统维护和使用,也可能由操作系统自己管理和维护;

  进程表:管理进程;

进程的位置

  进程包含一个或者一组被执行的程序,以及和这些程序相关的局部变量,全局变量,任何被定义的常量;此外,程序的执行通常涉及到用于跟踪过程调用和过程间参数的栈;最后,与每个进程相关的还有操作系统用于控制进程的许多属性,属性的集合被称为进程控制块。程序,数据,栈和属性的集合称为进程映像。

  

  进程映像的位置,依赖于使用内存管理方案。对于最简单的情况,进程映像保存在临近的或者连续的存储块中,进程映像保存在辅存中。因此操作系统要管理进程,其进程映像至少有一部分必须位于主存中。为了执行此进程,整个进程映像必须载入主存或者虚拟内存中。因此,操作系统需要知道每个进程在磁盘中的位置,并且对于在主存中的进程,操作系统需要知道其在主存中的位置。

  现代操作系统的分页硬件允许使用不连续的物理内存来支持部分常驻内存的进程。在任意时刻,进程映像的一部分可以在主存中,剩余部分可以在辅存中。因此,操作系统维护的进程表必须表明每个进程映像中每页的位置。

进程的属性

  进程标识号:每个进程都分配了唯一的数字标识号,它可以作为主进程表中的一个索引,也可以通过进程标识号定位相应的表。例如,内存表可以组织起来以便提供一个关于主存的映射,指明那个区域分配给那个进程。I/O表和文件表也有类似的引用。进程间相互通信时,进程标识号可用于通知操作系统某一个特定通信的目标。进程创建其他进程的时候,标识号可以用于指定每个进程的父进程和子进程。

  

  处理器状态信息:包括处理器的寄存器的内容。当进程在运行时,其信息在寄存器中。当进程被中断时,所有的寄存器信息必须保存起来,使的进程恢复执行时这些信息都可以恢复。所涉及的寄存器种类和数量取决于处理器的设计,典型情况下,寄存器组包括用户可见的寄存器,控制和状态寄存器,栈指针。所有的处理器设计都包括一个或一组通常称为程序状态字(PSW)的寄存器。

  

  进程控制信息:操作系统控制和协调活动进程所需要的额外信息。虚拟内存中的进程映像结构,包括进程控制块,用户栈,进程私有地址空间和与其他进程共享的地址空间。进程映像的地址是否连续取决于操作系统的内存管理方案和组织控制结构的方案。

  

进程控制块的作用

  进程控制块是操作系统中最重要的数据结构,操作系统中的每个模块都可能读取和修改他们。为了提供对进程控制块的保护,操作系统通过一个处理例程来专门处理,处理例程的任务是保护进程控制块,它是读写这些块的唯一仲裁程序。

进程控制

  执行模式:操作系统的某些指令,包括读取和改变程序状态字之类控制寄存器的指令,原始I/O指令和内存管理先关的指令,以及对部分内存区域的访问等,都必须在系统模式(也称为控制模式内核模式)下才能执行。与之对应的就是用户模式,用户程序通常运行在该模式下。

  

  使用两种的模式的作用是为了保护操作系统和操作系统的数据表(如进程表)不被用户程序影响。处理器如何区分当前进程的执行模式?通常情况下,程序状态字有一位表示执行模式,这一为会随着某些事件的发生而改变。例如,当用户程序调用一个系统服务或者中断触发系统例程的时候,执行模式位被置为内核模式,当从系统服务返回用户进程时,执行模式位被置为用户模式。

进程创建

  操作系统在创建进程时,会执行以下步骤:

  1. 给新进程分配一个进程标识号。主进程表中增加一个新表项;

  2. 给进程分配空间。包括进程映像中的所有元素:

    操作系统需要知道私有用户地址空间(程序和数据)和用户栈需要多少空间。这可以根据进程的类型使用默认值,也可以在作业创建时根据用户请求设置。如果是父进程   创建子进程,父进程可以把所需的值作为请求的一部分传给操作系统。如果有地址空间被新进程共享,必须建立正确的建立。

    给进程控制块分配空间;

  3. 初始化进程控制块

    进程标志号包括进程ID,父进程ID等;

    处理器状态信息大多被初始化为0,除了程序计数器(置为程序入口地址)和栈指针(定义进程栈边界);

    进程控制信息的初始化标准默认值和为该进程请求的属性。例如:进程状态在典型情况下被初始化成就绪或就绪/挂起态;优先级默认为最低优先级,除非显示请求更高   优先级;进程最初不拥有任何资源(I/0设备,文件),除非显示请求或从父进程处继承;

  4. 设置正确的链接。例如,新进程被放入就绪或者就绪/挂起队列中;

  5. 创建或扩充其他数据结构。例如,操作系统可能为每个进程保留一个审计文件,用于性能评估等;

进程切换

  进程切换的原因:

  

  两种系统中断:中断陷阱。两者的区别是:前者指与当前正在运行的进程无关的某种类型的外部事件发生,如一次I/O操作的完成;或者与当前正在运行的进程所产生的错误或异常条件相关,如非法的文件访问。

  对于普通中断,处理器控制权首先转移给中断处理器,它做一些基本的辅助工作,然后转到中断相关的操作系统例程中。例如:

    时钟中断:当前正在运行的时间超过了最大运行执行的时间段(时间片,进程在被中断前可以执行的最长时间);

    I/O中断:如果发生了I/O活动,操作系统把相应的阻塞态进程转换为就绪态进程,然后操作系统决定是让当前正在运行的进程继续执行,还是选择一个优先级更高的就   绪态进程执行;

    内存失效:处理器访问一个虚拟内存地址,且此地址单元不存在主存中时,操作系统就必须从辅存中将包含此地址单元的内存块(段或页)调入主存中。在发出内存调入的       I/O请求后,操作系统可能会执行进程切换,转而执行另一个进程。发生内存失效的进程被置为阻塞态,当需要的块调入内存中时,该进程被置为就绪态;

  对于陷阱,操作系统确定错误和异常条件是否是致命的。如果是,当前正在运行的进程被置为退出态,并发生进程切换;否则,操作系统的行为取决于错误的种类和操作系统的设计,其行为可能是试图恢复或通知用户,也可能是进行进程切换或者继续当前进程的执行。

  系统调用:如果一个正在运行的进程执行一个I/O请求的指令,如打开文件,这个调用将导致操作系统转到系统代码例程上执行。通常系统调用会阻塞正在运行的进程。

模式切换

  如果一个处理器发生了中断,处理器会执行以下操作:

    1. 把程序计数器置为中断处理器的开始地址;

    2. 把处理器模式从用户模式切换到内核模式,是中断处理代码可以执行特权指令;

  被中断进程的进程控制块中的处理器状态信息部分的数据必须保存,包括程序计数器,其他处理器寄存器和栈信息;

  中断处理器通常会做的事情:

    1. 给发生中断的实体(I/O模块)发送应答;

    2. 如果中断与I/O事件有关,中断处理器将检测错误条件;

    3. 如果发生错误,中断处理器给请求I/O操作的进程发一个信号;

    4. 如果是时钟中断,处理器控制权转移给分配器;

    5. 中断的发生并不是总是伴随进程的切换,可能在中断处理器执行完成后,继续执行被中断的进程;

完整的进程切换过程

  1. 保存处理器上下文,包括程序计数器和其他寄存器;

  2. 更新处于运行态的进程的进程控制块,包括把进程状态置为其他态等;

  3. 把进程的进程控制块移到相应的队列;

  4. 选择另一个进程执行;

  5. 更新选中的进程的进程控制块,包括更新进程的状态为运行态;

  6. 更新内存管理的数据结构;

  7. 恢复处理器在被选择的进程最近一次切换出运行态的上下文,通过载入程序计数器和其他寄存器的值来实现;

  因此,进程切换比模式切换需要做更多的工作。

 

操作系统的执行

  操作系统与普通软件一样,以同样的方式运行,也就是说由处理器执行一个程序;

  操作系统经常释放控制权,并且依赖处理机恢复控制权;

操作系统运行的各种方式:

  无进程的内核:许多老的操作系统中,通用的一种方式是在所有进程之外执行操作系统内核。通过这种方式,当前运行的进程发生中断或者产生一个系统调用时,该进程的模式上下文被保存起来,控制权转交给内核。操作系统有自己的内存区域和系统栈,用以控制过程调用和返回。操作系统可以执行任何预期功能,并恢复被中断进程的上下文,这就会导致被中断进程的继续执行。或者,操作系统可以完全保存进程环境的功能,并继续调度和分派另一个进程,是否这样做取决于中断原因和当前的情况。

  无论那种情况,关键点是进程的概念仅仅适用于用户程序,操作系统代码仅作为一个在特权模式下运行的独立实体被执行。

  

  在用户进程中执行:在较小的机器(PC,工作站)中,通用的方式是将操作系统软件作为用户进程调用的一组例程,在用户进程的上下文中执行所有操作系统软件。在任何时刻,操作系统管理着所有的进程映像,每个映像不仅包括用户进程自己的进程控制块,用户栈,私有用户地址空间(程序和数据),还包括内核程序的程序,数据和栈区域。

   

  一个典型的进程映像结构。当进程在内核模式下,独立的内核栈用于管理调用/返回。操作系统的代码和数据位于共享地址空间中,被所有用户进程共享。

   

  当发生一个中断,陷阱或者系统调用时,处理器被置为内核模式,控制权交给操作系统。为此,需要保存模式上下文并进行模式切换,然后切换到一个操作系统例程,但是,此时仍然在用户进程中执行,所以,这种方式不需要进行进程切换,仅仅进行了模式切换。在操作系统完成其操作后,如果需要继续运行当前进程,则再进行一次模式切换,在当前进程中恢复被中断的程序。这种方式的有点是,不需要两次进程切换,仅仅进行的是模式切换。如果确定需要发生进程切换而不是返回当前执行的程序,则控制权转交给进程切换例程,这个例程可能在当前进程中执行,也可能不在当前进程中执行。例如,当前进程必须置于非运行态,而另一个进程将指定为正在运行的进程。这样一个过程在逻辑上可以看作是在所有进程之外的环境中执行的。一个进程可以保存它的状态信息,从就绪态进程中选择另一个进程,并把控制权交个这个进程。这是一种混杂的情况,因为在关键时候,在用户进程中执行的是共享的操作系统的代码。即使这样,用户代码依然不能干扰操作系统的例程。

  进程和程序的概念是不同的,它们之间不是一对一的关系。在一个进程中,用户程序和操作系统的程序都可能执行,而在不同的用户进程中,操作系统的程序是相同的。

  基于进程的操作系统:把操作系统作为一组系统进程来实现。在这种情况下,主要的内核函数组织成独立的进程,同样,还可能有一些在任何进程之外执行的进程切换代码。这种方式的优点:

  利用程序设计的原理,将操作系统模块化,模块之间使用最简明的接口;

  一些非关键的操作系统函数可以独立成单独的进程;这个函数可以在指定的优先级上运行,并且在分派器的控制下和其他进程交替;

  操作系统作为一组进程实现,在多处理器和多处理机的环境中十分有用;

 

UNIX 系统

  UNIX系统V中,大部分操作系统在用户进程环境中执行,因此需要两种模式:用户模式和内核模式。UNIX使用两种:系统进程和用户进程。系统进程在内核模式下运行,执行操作系统代码以管理功能和内部处理,如内存分配和进程切换;用户进程在用户模式下运行,执行用户程序和实用程序,如果要执行内核的指令,需要切换到内核模式下运行。当产生异常,中断或系统调用时,用户进程进入内核模式。

  UNIX系统共有9中进程状态。

  

  UNIX采用两个运行态来区别表示进程是在用户模式下执行还是在内核模式下运行。被抢占态:当一个进程在内核模式下运行,内核完成了任务准备返还控制权给用户程序时,就可能出现被抢占的情况。这时候,内核可能抢占当前进程,运行另一个高优先级的进程。此时,该进程转到被抢占态,但是为了分配处理,被抢占态的进程和就绪态的进程处于同一个队列。当进程在内核模式下运行,是不能被抢占的,所以UNIX不适合实时处理。

  UNIX中有两个特殊的进程。

    进程0是在系统时创建的,它是一个预定义的数据结构,在启动时候被加载,是交换进程;

    进程1是由进程0创建的,是初始进程,是所有其他进程的祖先。当系统登陆到用户的时候,进程1为该用户创建一个用户进程。随后,用户进程可以创建子进程,从而构成一颗进程树。因此,任何应用程序都是一组相关进程组成;

  UNIX的进程描述:用户级上下文寄存器上下文系统级上下文

  

    用户级上下文:包括程序的基本部分,由编译好的目标文件直接产生。用户程序被分为正文和数据两个区域,正文区是只读的,用于保存程序指令。当进程执行时,处理器使用用户栈进行过程调用和返回,以及传递参数。共享内存区是和其他进程共享的内存区,它只有一个物理副本,但是通过使用虚拟内存,对每个共享的进程来说,共享内存区好像在他们各自的地址空间一样。

    寄存器上下文:当进程没有运行的时候,处理器的状态信息保存在寄存器上下文中。

    系统级上下文:包含操作系统管理进程所需要的其他信息,它有静态部分和动态部分组成。静态部分大小固定,而动态部分在整个生命周期中大小可变。

      静态部分中的动态部分:

      进程表项:它是操作系统维护的进程表中的一部分,每个进程对应表中的一部分。

    

      U(用户)区:包含内核在进程的上下文中执行时所需要的额外的进程控制信息。

    

      本进程区表:内存管理系统使用。

      系统级上下文的动态部分:内核栈。进程在内核模式下运行的时候,内核栈会包含过程调用和中断时必须保存和恢复的信息。

  UNIX系统通过内核系统调用fork()来创建进程。当进程产生一个fork请求,会执行以下步骤:

    1. 为新进程在进程表中分配一个空项;

    2. 为子进程赋值一个唯一的进程标识号;

    3. 创建一个父进程上下文的逻辑副本,不包括共享区;

    4. 增加父进程拥有的文件的计数器,以表示有另一个进程也拥有这些文件;

    5. 把子进程置为就绪态;

    6. 向父进程返回子进程的进程号,如果是子进程,返回零;

  以上操作都在内核模式下完成,当完成后可能继续以下三种操作之一(它们可以认为是分配器例程的一部分):

    1. 在父进程中继续执行。如果fork出返回的不是0,表示返回用户模式下的父进程fork调用处继续执行;

    2. 处理器控制权交给子进程。如果从fork处返回的值是0,表示在子进程中开始执行代码,同样是从fork调用处开始执行;

    3. 控制权转移给另外的进程,父进程和子进程都置为就绪态;

 

 

 

 

 

 

 

 

    

    

 

posted on 2014-12-01 19:55  黑铁时代  阅读(1416)  评论(0编辑  收藏  举报