帮助你详细了解进程

作者: 伊恩的道歉 | 来源:发表于2018-02-01 10:29 被阅读8次

    操作系统中最核心的概念是进程:这是对正在运行程序的一个抽象。操作系统的其他所有内容都是围绕着进程的概念展开的。

    即使可以使用的CPU只有一个,但他们也具有支持(伪)并发操作的能力,它们将一个单独的CPU变换成多个虚拟的CPU。可以说:没有进程的抽象,现代计算将不复存在。

    进程

    在任何多道程序设计系统中,CPU由一个进程快速切换至另一个进程,使每个进程各运行几十或几百毫秒。严格地说,在某一个瞬间,CPU只能运行一个进程。但在一秒钟内,它可能运行多个进程,这样就产生并行的错觉。有时人们所说的伪并行就是指这种情形,以此来区分多处理器系统(该系统有两个或多个CPU共享一个物理内存)的真正硬件并行。

    进程模型

    在进程模型中,所有可运行的软件,通常也包括操作系统,被组织成若干顺序进程,简称“进程”。一个进程就是一个正在执行程序的实例,包括程序计数器、寄存器和变量的当前值。从概念上说,每个进程有它自己的虚拟CPU。当然实际上真正的CPU在各个进程之间来回切换。这种快速切换,被称为多道程序设计。

    在下图(a)中可以看到,在一台多道程序计算机的内存中有四道程序。在下图(b)中,这4道程序被抽象为4个各自拥有自己控制流程(即每个程序自己的逻辑程序计数器)的进程,并且每个程序都独立地运行。当然实际上只有一个物理程序计数器,所以在每个程序运行时,它的逻辑程序计数器被装入实际的程序计数器中。当该程序执行结束(或暂停执行)时,物理程序计数器被保存在内存中该进程的逻辑程序计数器中。在下图c中可以看到,在观察足够长的一顿时间后,所有的程序都运行了,但在任何一个给定的瞬间仅有一个进程真正在运行。

    (a)含有4道程序的多道程序(b)4个独立的顺序进程模型(c)在任意时刻仅有一个程序是活跃的

    (a)含有4道程序的多道程序(b)4个独立的顺序进程模型(c)在任意时刻仅有一个程序是活跃的

    由于CPU在各进程之间来回快速切换,所以每个进程执行器运算的速度是不确定的。而且当同一进程再次运行时,其运算速度通常不可再现。所以在对进程编程时决不能对时序做任何想当然的假设。然而,通常大多数进程并不受CPU多道程序设计或其它进程相对速度的影响。

    进程和程序间的区别是非常微妙的,但非常重要。用一个比喻可以很容易理解这一点:想象有一位计算机科学家在位他的女儿烘制蛋糕。他有做生日蛋糕的食谱,厨房里有所需要的原料。则做蛋糕的食谱就是程序(即用适当形式描述的算法),计算机科学家就是CPU,厨房里的原料就是输入数据。进程就是厨师阅读食谱、取来原料以及烘制蛋糕等一系列动作的综合。

    关键思想:一个进程是某种类型的一个活动,它有程序、输入、输出以及状态。单个处理器可以被若干进程共享,它使用某种调度算法决定何时停止一个进程的工作,并转而为另一个进程提供服务。

    值得注意的是,如果一个程序运行了两遍,则算作两个进程。

    进程的创建

    在通用操作系统中,需要有某种方法在运行时按需要创建或撤销进程,现在开始考察这个问题。

    4种主要事件会导致进程的创建:

    (1)系统初始化

    (2)正在运行的程序执行了创建进程的系统调用

    (3)用户请求创建一个新的进程

    (4)一个批处理作业的初始化

    启动操作系统时,通常会创建若干个进程。其中有些是前台进程,也就是同用户(人类)交互并且替他们完成工作的那些进程。其他的是后台进程,这些进程与特定的用户没有关系,相反却具有某些专门的功能。停留在后台处理诸如电子邮件、web页面、新闻、打印之类的活动的进程称为守护进程。在大型系统中有很多守护进程。在UNIX中,可以用ps程序列出正在运行的进程;在Windows中,可使用任务管理器。

    一个正在运行的进程经常发出系统调用,以便创建一个或多个新进程协助其工作。在所要从事的工作可以很容易的划分出成个若干相关的但没有相互作用的进程时,创建新的进程就特别有效果。

    在交互式系统中,键入一个命令或点击一个图标就可以启动一个程序。这两个动作中的任何一个都会创建一个新的进程,并在其中运行所选择的程序。在基于命令行的UNIX系统中运行程序X,新的进程会从该进程接管它的窗口。

    最后一种创建进程的情形仅在大型机的批处理系统中应用。用户在这种系统中提交批处理作业。在操作系统认为有资源可运行另一个作业时,它会创建一个新的进程,并运行其输入队列中的下一个作业。

    从技术上看,在以上所有的情形中,新进程都是由于一个已经存在的进程执行了一个用于创建进程的系统调用而创建的。这个进程所做的工作是,执行一个用来创建进程的系统调用。这个系统调用通知操作系统创建一个新进程,并且直接或间接地指定在该进程中运行的程序。

    在UNIX系统中,只有一个系统调用可以用来创建新的进程:fork。这个系统调用会创建一个与调用进程相同的副本。在调用了fork之后,这两个进程(父进程和子进程)拥有相同的内存映像、同样的环境字符串和同样的打开文件。这就是全部情形。通常子进程接着执行execve或一个类似的系统调用,已修改其内存映像并运行一个新的程序。之所以要安排两步建立进程,是为了在fork之后但在execve之前允许该子进程处理描述其文件描述符,这样就可以完成对标准输入文件、标准输出文件和标准错误文件的重定向。

    在Windows中情形正好相反,一个Win32函数调用CreateProcess(该调用有10个参数)既处理进程的创建,也负责把正确的程序装入新的进程。除了CreatProcess,Win32中有大约100个其他的函数用于处理进程的管理、同步以及相关的事务。

    在UNIX和Windows中,进程创建之后,父进程和子进程各拥有不同的地址空间。如果其中某个进程在地址空间中修改了一个字,这个修改对其他进程而言是不可见的。在UNIX中,子进程的初始地址空间是个父进程的一个副本,但是这里涉及两个不同的地址空间,不可写的内存区是共享的。某些UNIX的实现使程序正文在两者之间共享,因为它不能被修改。或者,子进程共享父进程的所有内存,但这种情况先内存通过写实复制共享,这意味着一旦两者之一想要修改部分内存 ,则这块内存首先被明确地复制,以确保修改发生在私有内存区域。再次强调,可写的内存是不可以共享的。但是对于一个新创建的进程而言,确实有可能共享其创建者的其他资源,例如打开的文件等。在Windows中,从一开始父进程的地址空间和子进程的地址空间就不同。

    进程的终止

    新的进程的终止,通常由下列条件引起:

    (1)正常退出(自愿的)

    (2)出错退出(自愿的)

    (3)严重错误(非自愿的)

    (4)被其他进程杀死(非自愿的)

    多数进程是由于完成了它们的工作而终止。当编译器完成了所给定程序的编译之后,编译器执行一个系统调用,通知操作系统它的工作已经完成。在UNIX中该调用是exit,而在Windows中,相关的调用是ExitProcess。

    第二个原因是:进程发现了严重错误。在给出了错误参数时,面向屏幕的交互进程通常并不退出。相反,这些程序会弹出一个对话框,并要求用户再试一次。

    第三个原因:由进程引起的错误,通常是由于程序中的错误所致。例如执行了一条非法指令、引用不存在的内存、除数为0等。有些系统中(如UNIX),进程可以通知操作系统,它希望自行处理某些类型的错误,在这类错误中,进程会收到信号(被中断),而不是在这类错误出现时终止。

    第四个原因:某个进程执行一个系统调用通知操作系统杀死某个其他的进程。在UNIX中这个系统调用是kill,在Win32中对应的函数是TerminateProcess。在这两种情形中,“杀手”都必须获得确定的授权以便进行动作。(在Linux中一般要赋予root权限,才能执行kill操作)

    进程的层次结构

    某些系统中,当进程创建了另一个进程后,父进程和子进程就以某种形式继续保持关联。子进程自身可以创建更多的进程,组成一个进程的层次结构。进程只有一个父进程,但是可以有零个、一个、两个或多个子进程。

    在UNIX中,进程和它的所有子进程以及后裔共同组成一个进程组。当用户从键盘发出一个信号时,该信号被送给当前与键盘相关的进程组中的所有成员(它们通常是在当前窗口创建的所有活动进程)。每个进程可以分别捕获该信号、忽略该信号或采取默认的动作,即被该信号杀死。

    考虑UNIX系统在启动时如何初始化自己。一个称为init的特殊进程出现在启动映像中,当它开始运行时,读入一个说明终端数量的文件。接着,为每个终端创建一个新进程。这些进程等待用户登录。如果有一个用户登录成功,该登陆进程就执行一个shell准备接收命令。所接收的命令会启动更多的进程,以此类推,这样整个系统中,所有的进程都属于以init为根的一棵树。

    相反,在Windows中没有进程层次的概念,所有的进程地位都是相同的。唯一类似于进程层次的暗示是在创建进程的时候,父进程得到一个特别的令牌(称为句柄),该句柄可以用来控制子进程。但是,它有权把这个令牌传送给某个其他的进程,这样就不存在进程层次了。在UNIX中,进程就不能剥夺其子进程的“继承权”。

    进程的状态

    尽管每个进程是一个独立的实体,有其自己的程序计数器和内部状态,但是进程之间经常需要相互作用。一个进程的输出结果可能作为另一个进程的输入。当一个进程在逻辑上不能继续运行时,他就会被阻塞,典型的例子是它在等待可以使用的输入。还可能有这样的情况:一个概念上能够运行的进程被迫停止,因为操作系统调度另一个进程占用了CPU。这两种情况是完全不同的。在第一种情况下,进程挂起是程序固有的原因(在键入用户命令行之前,无法执行命令)。第二种情况则是由于系统技术上的原因引起的(由于没有足够的CPU,所以不能使每个进程都有一台私用的处理器)。在图1中可以看到显示进程的三种状态的状态图,这三种状态是:(1)运行态(该时刻进程实际占用CPU)(2)就绪态(可运行,但因为其它进程正在运行而暂时停止)(3)阻塞态(除非某种外部事件发生,否则进程无法运行)。

    图1 一个进程可处于运行态、阻塞态和就绪态,图中显示出个状态之间的转换

    图1 一个进程可处于运行态、阻塞态和就绪态,图中显示出个状态之间的转换

    进程的三种状态之间有四种可能的转换关系。如图1所示。在操作系统发现进程不能运行下去时,发生转换1。在某些系统中,进程可以执行一个诸如pause的系统调用来进入阻塞状态。

    转换2和3是由进程调度程序引起的,进程调度程序是操作系统的一部分,进程甚至感觉不到调度程序的存在。系统认为一个运行进程占用处理器的时间已经过长,决定让其他进程使用CPU时间时,会发生转换2.在系统已经让所有其他进程享有了他们应当应有的公平待遇而重新轮到第一个进程再次占用CPU时间时,会发生转换3。

    当进程等待的一个外部事件发生时(如一些输入到达),则发生转换4.如果此时没有其他进程运行,则立即触发转换3,该进程便开始运行。否则该进程将处于就绪态,等待CPU空闲并且轮到它运行。

    调度程序的主要工作就是决定应当运行哪个进程、何时运行及它应该运行多长时间,这是很重要的一点。

    进程的实现

    为了实现进程模型,操作系统维护着一张表格(一个结构数组),即进程表。每个进程表占用一个进程表项。(有时也称进程控制块)该表项包含了进程状态的重要信息,包括程序计数器、堆栈指针、内存分配状况、所打开文件的状态、账号和调度信息,以及其他在进程由运行态转换到就绪态或阻塞态时所必须保存的信息,从而保证该进程随后能再次启动,就像从未中断过一样。

    图2展示了在一个典型系统中的关键字段。

    图2 典型的进程表表项中的一些字段

    图2 典型的进程表表项中的一些字段

    与每一I/O类关联的是一个称作中断向量的位置(靠近内存底部的固定区域)。它包含中断服务程序的入口地址。假设当一个磁盘中断发生时,用户进程3正在运行,则中断硬件将程序计数器、程序状态字、有时还有一个或多个寄存器压入栈堆,计算机随机跳转到中断向量所指示的地址。这些是硬件完成的所有操作,然后软件,特别是终端服务例程就接管一切剩余的工作。

    所有的中断都从保存寄存器开始,对于当前进程而言,通常是保存在进程表项中。随后,会从堆栈中删除由中断硬件机制存入堆栈的那部分信息,并将堆栈指针指向一个由进程处理程序所使用的临时堆栈。一些诸如保存寄存器值和设置堆栈指针等操作,无法用C语言这一类高级语言来描述,所以这些操作通常通过一个短小的汇编语言里程来完成,通常该例程可以供所有的中断来完成,因为无论中端是怎样引起的,有关保存寄存器的工作则是完全一样的。

    当该例程结束后,它调用一个C过程处理处理某个特定的中断类型剩下的工作。在完成有关工作之后,大概就会使某些进程就绪接着调用调度程序,决定随后该运行哪个进程。随后将控制转给一段汇编语言代码,为当前的进程装入寄存器值以及内存映射并启动该进程运行。

    中断发生后操作系统最底层的工作步骤

    中断发生后操作系统最底层的工作步骤

    多道程序设计模型

    采用多道程序设计可以提高CPU的利用率。严格的说,如果进程用于计算的平均时间是进程在内存中停留时间的20%,且内存中同时有5个进程,则CPU将一直满负载运行。然而,整个模型在现实中过于乐观,因为它假设这5个进程不会同时等待I/O。

    更好的模型是从概率的角度来看CPU的利用率。假设一个进程等待I/O操作的时间与其停留在内存中的时间比为p。当内存中同时有n个进程时,则所有n个进程都在I/O的概率是Pn(幂指数)。CPU的利用率由下面的公式给出:

    CPU利用率 = 1-Pn

    图3以n为变量的函数表示了CPU的利用率,n称为多道程序设计的道数。如果进程花费80%的时间等待I/O,为使CPU的浪费低于10%,至少要有10个进程同时存在内存中。

    进程不是独立的。更精确的模型应该由排队论构建,但我们的模型(当进程就绪时,给进程分配CPU,否则让CPU空转)仍然是有效的,即使真实曲线会与图3略有不同。

    图3对预测cpu的性能很有效。例如:假设计算机有8GB的内存,操作系统及相关表格占用2GB,每个用户程序也占用2GB。这些内存空间允许3个用户程序同时驻留在内存中。若80%的时间用于I/O等待,这CPU的利用率大约是1-(0.80.80.8),即大约是49%.在增加8GB的内存后可从3道程序设计提高到7道程序设计,此时CPU的利用率提高到79%。提高了30%的吞吐量。增加第三个8GB的内存,CPU的利用率提高到91%,吞吐量的提高仅为12%.则第二次增加内存是不划算的。

    因此,盲目提高内存,有时提高不了多少性能,它还与系统等待I/O的时间、CPU的性能有关。

    CPU利用率是内存中进程数目的函数

    CPU利用率是内存中进程数目的函数

    相关文章

      网友评论

        本文标题:帮助你详细了解进程

        本文链接:https://www.haomeiwen.com/subject/cszrzxtx.html