进程模型
一台电脑一般会“同时”运行很多程序,这些程序也包括操作系统自身,但是CPU只有一个(先不考虑多核情况),那么如何同时运行这些程序呢?其实这是不可能的,在某一时刻只能有一个程序在CPU上运行,那么如何实现我们想要的“同时”呢?答案是:分时复用
。由于CPU的运行速度很快,我们可以让它在多个程序之间快速地切换,这样就会给人造成一种假象,仿佛这些程序是同时运行的,也就是我们所说的伪并行,这就是进程模型
。说完什么是进程,接下来我们就来看看这种模型是如何实现的。
进程的创建
一般的操作系统都需要某种方法来创建或撤销进程,有四种主要事件导致进程的创建:
- 系统初始化
- 执行了正在运行的进程所调用的进程创建
系统调用
- 用户请求创建一个新进程
- 一个批处理作业的初始化
从技术上看,在这些情形中,新进程都是由于一个已经存在的进程执行了一个用于创建进程的系统调用而创建的。
在UNIX中,用fork来创建系统调用,调用了fork之后,这个系统调用会创建一个与调用该系统调用的进程相同的副本,这两个进程(父进程和子进程)拥有相同的存储映像,同样的环境字符串和同样的打开文件,接着子进程继续执行execve或一个类似的系统调用以修改其存储映像并运行一个新的程序。
进程的终止
永恒是不存在的,进程创建了就是要被终止的,进程创建通常由下列事情引起
- 正常退出
- 出错退出
- 严重错误
- 被其它进程杀死
进程的层次结构
父进程创建子进程,子进程又创建自己的子进程,一直下去就会产生一个进程树,这就是进程的层次结构
在UNIX中是存在这样一个进程层次的模型的,但是在windows中是没有这样的模型的,在windows中,所有的进程的地位是相同的,在进程创建的时候,父进程会得到一个令牌用来控制子进程,但是父进程是可以把这个令牌交给其他进程的,这样就不存在层次结构了。但在UNIX中,父进程是不能随意转交子进程的控制权的。
进程的状态
进程的四种状态
- 运行态
- 就绪态
- 阻塞态
上面是所有操作系统都必须实现的三种状态,但不同的操作系统在进程状态上的设计多少会有些不一样的。
运行态是指进程正在占用CPU,就绪态是指进程可以运行,但是CPU此时被其它进程占用,阻塞态是指进程暂时无法执行(正在等待输入参数),即使CPU空闲也不行。
这里面有四种转换关系
- 当进程由于阻塞(如等待I/O)无法继续运行时,会发生转换
1
2
和3
是由于进程调度程序引起的,当系统认为一个程序运行时间过长就会让它从运行态进入就绪态,当系统认为一个程序应该运行了,就将它从就绪态变成运行态。- 当进程等待的一个外部事件发生时(例如键盘输入到达)就发生转换
4
,如果当前CPU没有被占用就立即进入运行态。
基于以上观点我们实现了一种程序调度模型:最底层是调度程序,上面是一个个顺序进程,所有中断处理,启动进程,终止进程的具体细节都隐藏在调度程序中,虽然这个模型很简单,但是很少有实际的操作系统是以这样理想的方式实现的(基于实际情况的复杂性,我们需要更多的机制与策略来应对)。
进程的实现
为了实现进程模型,操作系统维护着一张表格(一个结构数组),叫做进程表,每个进程在里面拥有一个进程控制块(也叫进程表项),里面大概包含以下内容。这些内容和系统密切相关,这只是大致介绍。
进程管理 | 存储管理 | 文件管理 |
---|---|---|
寄存器 | 正文段指针 | 根目录 |
程序计数器 | 数据段指针 | 工作目录 |
程序状态字 | 堆栈段指针 | 文件描述符 |
堆栈指针 | 用户ID | |
进程状态 | 组ID | |
优先级 | ||
调度参数 | ||
进程ID | ||
父进程 | ||
进程组 | ||
信号 | ||
进程开始时间 | ||
使用的CPU时间 | ||
子进程的CPU时间 | ||
下次报警时间 |
如果把进程比作人,那么进程控制块相当于人的身份证,我们可以通过身份证检查与控制这个人的一切社会活动。