本文若未注明均摘抄自《操作系统精髓与设计原理》:William Stallings著,陈向群、陈渝译,机械工业出版社出版第六版
3 进程描述和控制
关键字 | |||
---|---|---|---|
阻塞态 | 父进程 | 进程切换 | 交换 |
子进程 | 抢占 | 程序状态字 | 内核态 |
退出态 | 任务 | 就绪态 | 中断 |
进程 | 轮转 | 跟踪 | 进程控制块 |
用户态 | 运行态 | 陷阱 | 模式切换 |
进程映像 | 挂起态 | 新建态 |
3.1 什么是进程
3.1.1 进程和进程控制块
进程的两个基本元素是程序代码和代码相关的数据集。
注:举例说明的话,熟悉 C/C++ 的读者可能了解,在经由 C/C++ 编译的程序在运行时的内存布局分为以下几个部分
1. 栈区(stack)由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈
2. 堆区(heap) 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。
3. 全局区(静态区)(static),全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 - 程序结束后有系统释放
4. 文字常量区 —常量字符串就是放在这里的。 程序结束后由系统释放
5. 程序代码区—存放函数体的二进制代码。
这其中的5就是所谓的进程的程序代码,而1-4就是所谓的代码相关的数据集。这两种概念间微妙的差别需要读者细细品味
以上从程序运行的视角来对进程的内存进行了一番管中窥豹。如果从操作系统的角度观察的话,进程的内存中还有更多的细节需要注意。
在进程执行时,任何一个时间进程的所有细节都可以被以下元素唯一的表达
- 标识符
- 状态
- 优先级
- 程序计数器
- 内存指针:包括程序代码和进程相关数据的指针
- 上下文数据:进程执行时处理器中寄存器的数据
- I/O状态信息:包括显式的I/O请求、分配给进程的I/O设备(例如磁盘驱动器)和被进程使用的文件列表等
- 记账信息:可能包括处理器时间总和、使用的时钟数总和、时间限制、记账号等
前述的列表信息存放在一个叫做进程控制块(如下图)的数据结构中
进程控制块是操作系统能够支持多进程和提供多处理的关键概念,可以说进程是有程序代码和相关进程控制块组成。
3.2 进程状态
单个进程的行为可以被一串指令序列描述,这个序列被称为进程的轨迹,这个概念的含义就如同看上去那样肤浅,只是起了个名字而已。
考虑一个非常简单的例子,假设有轨迹如下的三个进程A、B、C
现在从处理器的角度来看这些轨迹。为了简化讨论,假设这些进程没有使用虚拟内存——也就代表这些进程都完全被载入到了内存中,并且假设这些进程不会调用I/O设备,也即不会长时间的等待响应。此外,存在一个调度器使处理器从一个进程切换到另外一个进程。
在图3.4中,显示了这三个进程被处理器“同时”处理时可能出现的处理过程,其中阴影部分代表由调度器执行的代码。
博主认为这个示例和两张图尤其值得仔细理解
3.2.1 两状态进程模型
在这种最简单的模型中,一个进程可以处于两种状态中的一种:运行和未运行。这种模型虽然简单,但所有现代的进程模型特性都是围绕这个中心发展而来的。一个典型的两状态进程模型如下图所示:
3.2.2 进程的创建和终止
在对上述最简单的进程模型进行扩充之前,需要先讨论一下进程的创建和终止的细节
进程的创建
如3.1.1所述,当一个新进程被创建时,操作系统需要在进程列表中为它创建一个与其他进程格式相同的数据结构用于记录和管理它的状态。
通常有四种事件会导致进程的创建
表3.1 导致进程创建的原因
事件 | 说明 |
---|---|
新的批处理作业 | 通常位于磁带或者磁盘中的批处理作业控制流程被提供给操作系统。当操作系统准备接纳新工作时,它将读取下一个作业控制命令 |
交互登陆 | 终端用户登录到系统 |
操作系统因为提供一项服务而被创建 | 操作系统可以创建一个进程,代表用户程序执行的一个功能,使用户无需等待(如控制打印机的进程) |
由现有的进程派生 | 基于模块化的考虑,或者为了开发并行性,用 |