进程, 是操作系统提供的基本抽象, 是运行中的程序. 程序本身没有生命周期, 只是存储在磁盘上的一些指令(或者一些静态数据).
是操作系统让这些字节运行起来, 让程序发挥作用.

人们希望计算机可以同时运行多个程序, 因此我们希望实现这样的系统: 提供给程序有无数个 CPU 可用的假象,
从而使得人们在程序运行时不需要考虑哪个 CPU 是可用的.

也就是通过虚拟化 CPU 来实现. 让一个进程只运行一个时间片, 然后切换到其他进程, 从而操作系统提供了存在多个虚拟 CPU 的假象
— 也就是时分共享(time share) CPU 技术.

操作系统通过:

  • 低级机制, 如上下文切换.
  • 高级智能, 一些以策略的形式存在, 是做出决定的某种算法, 例如调度策略.

进程

操作系统为正在运行的程序提供的抽象, 即为所谓进程. 我们可以通过他在执行过程中访问或影响的系统的不同部分来概括他, 理解其组成, 也就是:

核心概念: 机器状态(machine status): 程序在运行时可以读取和更新的内容, 包括:

  1. 地址空间: 进程可以访问的内存.
  2. 寄存器
  3. 特别地, 一些特殊的寄存器: PC(程序计数器), SP(栈指针)
  4. 当前打开的文件列表: 关系程序访问持久存储设备的 I/O 信息.

实际上,
进程 ≈ 机器状态 + 操作系统管理信息(PCB 等) + 执行行为
机器状态是一个进程的静态部分, 会随着进程的进行而变化, 例如开关文件, 动态分配内存等. 可以理解为是进程的一个snapshot.

进程 API

所有操作系统都必须以某种形式提供这些 API, 以对进程进行操作.

  • 创建(create)
  • 销毁(destroy): 很多进程会在运行结束后自行退出, 但是用户可能想要终止, 因此要有销毁的接口.
  • 等待(wait): 等待进程停止运行.
  • 其他控制(miscellaneous control): 例如暂停, 恢复.
  • 状态(status): 获取有关进程的状态信息, 例如运行时间与当前状态.

进程创建

程序如何转化为进程? 也就是操作系统如何启动并运行一个程序?

  1. 将代码和所有静态数据(例如初始化变量), 从磁盘中读取, 并加载到内存, 加载到进程的地址空间.
    现代操作系统 lazily 执行改过程, 随用随加载, 将在后续的内存虚拟化进一步讨论.
  2. 为程序的运行时栈分配内存. 也可能用参数初始化栈, 把参数(argc, argv[])填入 main()
  3. 也可能为堆分配一些内存. 起初堆很小, 会随着程序运行, 通过 malloc() 库 API 请求更多内存,
    操作系统可能会参与分配更多内存给进程.
  4. 其他初始化任务, 特别是 I/O 相关任务.
  5. 启动程序, 在入口处(main() 函数)运行.

Loading: From Program to Process

进程状态

进程可以处于以下三种状态之一:

  • 运行(running): 进程正在处理器运行, 正在执行指令.
  • 就绪(ready): 进程已准备好运行, 但是因某种原因, 操作系统选择此时不执行.
  • 阻塞(blocked): 进程执行了某种操作, 知道发生其他事件后才会准备运行(进入就绪状态).

可以根据操作系统的裁量, 让进程在就绪和运行状态间切换, 这称为调度取消调度.

Process: State Transition

数据结构

操作系统是一个程序, 有一些关键的数据结构来跟从各种相关信息.
例如操作系统可能有一个列表, 记录所有就绪的进程; 以及跟踪当前运行的进程的一些附加信息.

人们会把存储关于进程信息的个体结构称为进程控制块(PCB), 这是谈论包含每个进程信息的 C 结构的一种方式.