进程相关知识

进程

当启动系统时,会秘密启动许多进程。例如,启动一个进程用来等待进入的电子邮件;或者启动另一个防病毒进程周期性地检查是否有病毒库更新。于是一个支持多进程的多道程序系统在这里就显得很有用了。

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

伪并行:是指单核或多核处理器同时执行多个进程,通过以非常有限的时间间隔在程序之间快速切换CPU,因此会产生并行感。缺点是CPU时间可能分配给下一个进程,也可能不分配给下一个进程。

进程模型

在进程模型中,计算机上所有可运行的软件,通常也包括操作系统,被组织成若干顺序进程,简称进程。一个进程就是一个正在执行程序的实例,包括程序计数器,寄存器和变量的当前值。从概念上说,每个进程拥有它自己的虚拟CPU。当然,实际上真正的CPU在各进程之间来回切换。这种快速的切换叫做多道程序设计
在这里插入图片描述
在图2-1a中可以看到,在一台多道程序计算机的内存中有4道程序。在图2-1b中,这4道程序被抽象化为4个各自拥有自己控制流程(即每个程序自己的逻辑程序计数器)的进程,并且每个程序都独立地运行。当然,实际上只有一个物理程序计数器,所以在每个程序运行时,它的逻辑程序计数器被装入实际的程序计数器中。当该程序执行结束(或暂停执行)时,物理程序计数器被保存在内存中该进程的逻辑程序计数器中。在图2-1c中可以看到,在观察足够长的一段时间后,所有的进程都运行了,但在任何一个给定的瞬间仅有一个进程真正在运行

一个CPU只能真正一次运行一个进程,即使有2个核,每一个核也只能一次运行一个进程

由于CPU在各进程之间来回快速切换,所以每个进程执行期运行的速度是不确定的。而且当同一进程再次运行时,其运算速度通常也不可再现。所以,在对进程编程时绝不能对时序做任何想当然的假设。

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

如果一个程序运行了两遍,则算作两个进程

进程的创建

4个事件会导致进程的创建。

  • 系统初始化。
  • 正在运行的程序执行了创建进程的系统调用。
  • 用户请求创建一个新进程。
  • 一个批处理作业的初始化。

系统初始化

启动操作系统时,通常会创建若干个进程。其中有些是前台进程,也就是同用户进行交互并替它们完成工作的进程。一些运行在后台,并不与特定的用户进行交。进程运行在后台用来处理一些活动像e-mail,web网页,新闻等等被称为守护进程。在大型系统中通常有很多守护进程。在UNIX中,可以用ps命令列出正在运行的进程。

系统调用创建

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

用户请求创建一个新进程

在交互式系统中,键入一个命令或者点击一个图标就可以启动一个程序。这两个动作中的任何一个都会开始一个新的进程,并在其中运行所选择的程序。

批处理创建

用户在大型机的批处理系统中提交批处理作业。

在操作系统认为有资源可运行另一个作业时,它创建一个新的进程,并运行其输入队列中的下一个作业。


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

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

在Windows中,从一开始父进程的地址空间和子进程的地址空间就是不同的。

进程的终止

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

  • 正常退出(自愿的)。
  • 出错退出(自愿的)。
  • 严重错误(非自愿)。
  • 被其他进程杀死(非自愿)。

正常退出

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

出错退出

进程发生终止的第二个原因是进程发现了严重的错误。例如执行程序,但是该程序不存在,编译器就会退出。

严重错误

第三个原因是由进程引起的错误,通常是由于程序中的错误所致。例如执行了一条非法指令,引用不存在的内存,或是除数是零。在这类错误中,进程会收到信号(被中断),而不是在这类错误出现时终止。

被其他进程杀死

某个进程执行一个系统调用通知操作系统啥死某个其他进程。在UNIX中,这个系统调用是kill

进程的层次结构

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

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

这里有另一个例子,可以用来说明进程层次的作用,考虑UNIX在启动时如何初始化自己。一个称为init的特殊进程出现在启动映像中。当它开始运行时,读入一个说明终端数量的文件。接着,为每个终端创建一个新进程。这些进程等待用户登录。如果有一个用户登录成功,该登录进程就执行了一个shell准备接受命令。所接受的这些命令会启动更多的进程。

这样,在整个系统中,所有的进程都属于以init为根的一棵树

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

进程的状态

尽管进程是一个独立的实体,有其自己的程序计数器和内部状态,但是,进程之间经常需要相互作用。一个进程的输出结果可能作为另一个进程的输入。

shell命令

cat chapter1 chapter2 chapter3 | grep tree

中,第一个进程运行cat,将三个文件连接并输出。第二个进程运行grep,它从输入中选择所有包含单词tree的那些行。根据这两个进程的相对速度,可能发生这种情况:grep准备就绪可以运行,但输入还没有完成,于是必须阻塞grep,直到输入到来。

当一个进程在逻辑上不能继续运行时,它就会被阻塞,典型的例子是它在等待可以使用的输入。还有可能有这样的情况:一个概念上能够运行的进程被迫停止,因为操作系统调度另一个进程占用了CPU。

在这里插入图片描述

  • 运行态(该时刻进程实际占用CPU)。
  • 就绪态(可运行,但因为其他进程正在运行而暂时停止)。
  • 阻塞态(除非某种外部事件发生,否则进程不能运行)。

前两种状态在逻辑上是类似的。处于这两种状态的进程都可以运行,只是对于第二种状态暂时没有CPU分配给它。第三种状态与前两种状态不同,处于该状态的进程不能运行,即使CPU空闲也不行。

进程的三种状态之间有四种可能的转换关系,如图所示,在操作系统发现进程不能继续的运行下去时,发生转化1。在某些系统中,进程可以执行一个诸如pause的系统调用来进入阻塞状态。在其他系统中,包括UNIX,当一个进程从管道或设备文件读取数据时,如果没有有效的输入存在,则进程会被自动阻塞。

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

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

在这里插入图片描述

操作系统最底层的就是调度程序,在它上面有许多进程。所有关于中断处理,启动进程和停止进程的具体细节都隐藏在调度程序中。

进程的实现

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

进程管理存储管理文件管理
寄存器正文段指针根目录
程序计数器数据段指针工作目录
程序状态字堆栈段指针文件描述符
堆栈指针用户ID
进程状态组ID
优先级
调度参数
进程ID
父进程
进程组
信号
进程开始时间
使用的CPU时间
子进程的CPU时间
下次定时器时间

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

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

  • 硬件压入堆栈程序计数器等。
  • 硬件从中断向量装入新的程序计数器。
  • 汇编语言过程保存寄存器值。
  • 汇编语言过程设置新的堆栈。
  • C中断服务例程运行。
  • 调度程序决定下一个将运行的进程。
  • C过程返回至汇编代码。
  • 汇编语言过程开始运行新的当前进程。

一个进程在执行过程中可能被中断数千次,但关键是每次中断后,被中断的进程都返回到与中断发生前完全相同的状态

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值