# 进程 (Process)

进程是可并发执行的程序在某个数据集合上的一次计算活动,也是操作系统进行资源分配和保护的基本单元

进程与程序的区别

  • 进程是正在执行的程序
  • 程序是非活跃实体,例如磁盘中存储的可执行文件
  • 进程是活跃体,是可以被赋予处理器资源并在其运行的实体

# 进程的描述和组成 -- 进程映像

  • 程序代码
  • 数据
  • 堆栈
  • 与进程相关信息
    • 标识符
    • 状态
    • 优先级
    • 程序计数器

# 进程上下文

进程物理实体和支持进程运行的环境合称为进程上下文,包含所有运行程序所需要的状态信息。

  • 用户级上下文:包括了代码、数据、堆栈
  • 寄存器上下文:通用寄存器、程序计数器、栈指针、程序状态字
  • 系统级上下文:
    • 进程标识信息
    • 进程调度的相关信息,如进程优先级
    • 打开的文件列表
    • 存储管理相关的信息,如段 / 页表指针
    • 分配的设备列表
    • 记账信息,如已执行的时间总和

用户级上下文存储在内存中,其余上下文信息存储在进程控制块中

# 进程控制块

每个进程有且仅有一个进程控制块 PCB,是操作系统用来记录和刻画进程状态及有关信息的数据结构。

  • 标识信息
    • 进程标识 ID
    • 进程组标识 ID
  • 现场信息
    • 程序计数器
    • CPU 寄存器
  • 控制信息
    • 进程状态
    • CPU 调度信息:如优先级,调度队列指针
    • 内存管理信息
    • 计费信息:进程使用的 CPU 时间
    • I/O 状态信息:分配给进程的 I/O 设备,文件列表

# 进程切换

进程切换发生在操作系统从当前运行进程获得控制权后的任意时刻,系统处于核心态时进行。

操作系统获得控制权的方式有中断和系统调用两种方式。

进程切换机制

  • 保存上下文(Context Save): 当一个进程被中断或时间片用完时,操作系统会保存该进程的当前状态(寄存器、程序计数器等)到进程控制块(PCB)中。
  • 加载新上下文(Context Restore): 然后,操作系统会加载新要执行的进程的上下文,将其之前保存的状态还原,使其继续执行。

# 中断

# 时钟中断

操作系统确定当前运行进程的时间片是否已到,如果是,则当前进程必须切换到就绪态,而调度其它进程执行

# I/O 中断

操作系统确定当前 I/O 操作的类型。如果所产生的 I/O 事件是一个或多个进程正在等待的,则这些被阻塞进程将被移动到就绪态。操作系统接着必须决定是重启被暂停的进程,还是用更高优先级的就绪进程抢占当前进程

# 缺页中断

处理器遇到虚拟内存地址不在主存中的错误。则操作系统必须将被访问的块(页或段)载入内存。当将其载入内存的 I/O 请求被发出后,发生内存缺页的进程将处于阻塞态,操作系统随后进行进程切换执行另一个进程。当所需要的块载入内存完毕后,对应的进程被置于就绪状态

# 程序性中断

操作系统确定所发生的错误或异常是否是致命的。若是,则当前运行进程将被置为 Exit 状态,并进行进程切换;若不是致命错误,则操作系统的行为依赖于错误的性质和操作系统的设计。它可能会尝试一些恢复性操作或简单地通知用户,或者它可以进行进程切换或重启当前运行进程

# 进程状态

# 五态模型

只有就绪态和运行态可以相互转换,其它的都是单向转换。就绪状态的进程通过调度算法从而获得 CPU 时间,转为运行状态;而运行状态的进程,在分配给它的 CPU 时间片用完之后就会转为就绪状态,等待下一次调度。
阻塞状态是缺少需要的资源从而由运行状态转换而来,但是该资源不包括 CPU 时间,缺少 CPU 时间会从运行态转换为就绪态。

  • 新建态:进程刚被创建时,尚未进入就绪队列

  • 就绪态:进程具备运行条件

  • 运行态:进程占用处理器运行

  • 等待态:进程不具备运行条件

  • 终止态:进程完成执行

# 七态模型

受限于主存资源限制,将某些进程挂起,并置于磁盘对换区

等待→挂起等待

  • 不存在就绪进程
  • 当前运行进程或即将运行的就绪进程需要足够多的内存空间以保持性能

挂起等待→挂起就绪

  • 当等待的事件发生

挂起就绪→就绪

  • 当前主存中不存在就绪进程
  • 挂起就绪态进程比主存中所有就绪态进程的优先级都高

就绪→挂起就绪

  • 只有将某个就绪进程挂起才能释放足够多的内存空间
  • 挂起一个低优先级的就绪进程可能比挂起高优先级的阻塞进程更有利

挂起等待→等待

  • 当某个进程结束释放部分主存,同时挂起等待队列中的某个进程比挂起就绪队列中的所有进程优先级都高,并且 OS 认为等待事件即将发生。

运行→挂起就绪

  • 当一个具有高优先级的挂起等待态进程所等待的事件发生,操作系统决定抢占当前运行进程,则可以直接将当前进行变为挂起就绪态,释放部分内存空间

# 进程队列

把处于同一状态的所有进程的PCB链接在一起的数据结构称为进程队列

物理实现:

  • 线性方式
  • 链接方式
  • 索引方式

# 模式切换和进程切换

# 模式切换

当中断、异常或系统调用发生时,需要暂停当前正在运行的进程,把处理器状态从用户态切换到核心态,执行操作系统服务程序

# 进程的控制和管理

# 进程创建

# 创建新进程的原因

  • 提交批处理作业
  • 交互式作业登录
  • 操作系统创建服务进程
  • 已存在的进程创建新进程

# 创建过程

  • 在进程表中增加一项,从 PCB 池中申请一个空闲 PCB,为新进程分配唯一的进程标识符
  • 为新进程的进程映像分配地址空间,以容纳进程实体
  • 为新进程分配除主存空间以外的各种其他资源
  • 初始化 PCB
  • 把新进程的状态设为就绪态,并将其移动至就绪队列
  • 将该进程创建事件通知操作系统的某些模块
    进程用进程标识符PID唯一标识

资源共享:

  • 父进程和子进程共享所有资源
  • 子进程共享父进程的部分资源
  • 子进程和父进程不共享任何资源

执行:

  • 父进程和子进程并发执行
  • 父进程等待子进程执行结束后继续执行

地址空间:

  • 子进程是父进程地址空间的拷贝
  • 子进程在地址空间中载入新程序

# 进程撤销

# 进程撤销的原因

  • 进程运行结束
  • 进程执行非法指令
  • 进程在用户态执行特权指令
  • 进程的运行时间超过所分配的最大时间配额
  • 进程的等待时间超过所设定的最长等待时间
  • 进程所申请的主存空间超过系统所能提供的最大容量
  • 越界错误
  • 对共享主存区的非法使用
  • 算术错误
  • I/O 操作故障
  • 父进程撤销
  • 操作系统终止

# 撤销过程

  • 根据进程标识号从相应的队列中移除它
  • 将进程所拥有的资源归还给父进程或操作系统
  • 若此进程拥有子进程,先撤销其所有子进程,以防止它们脱离控制
  • 回收 PCB,并将其归还至 PCB 池

# 进程阻塞和唤醒

# 进程阻塞

  • 停止进程执行,将现场信息保存到 PCB
  • 修改进程 PCB 的有关内容,如进程状态从运行态变为等待态
  • 把进程移入相应时间的等待队列中
  • 转入进程调度程序,调度其他程序执行

# 进程唤醒

  • 从相应的等待队列中移出进程
  • 修改进程 PCB 的相关内容,如进程状态改为就绪态,并将进程移入就绪队列
  • 若被唤醒的进程比当前运行进程优先级高,且采用抢占式调度,则进行进程调度

# 进程挂起和激活

# 进程挂起的步骤

  • 修改进程的状态,如为活动就绪态,改为挂起就绪态,如为等待态,改为挂起等待态
  • 将被挂起进程 PCB 非常驻部分交换到磁盘对换区

# 进程激活的步骤

  • 将被挂起进程 PCB 非常驻部分调入内存
  • 修改进程的状态,并将进程移入相应的队列

# 线程 (Thread)

线程是调度执行的基本单元,而进程是资源分配的主体

  • 多线程是指在一个进程中多个线程并发执行

线程的组成部分

  • 线程标识符
  • 线程执行状态
  • 线程不运行时的线程上下文
  • 核心栈
  • 与每个线程相关的局部变量

# 线程与进程的区别和联系

区别

  1. 独立性:
    • 进程是系统资源分配的基本单位,具有独立的内存空间、文件句柄等资源,每个进程有自己的地址空间。进程之间不能直接共享变量等数据,需要通过进程间通信机制进行通信。
    • 线程是进程内的执行单元,共享同一进程的内存空间和资源。线程之间可以直接共享变量等数据,因此线程间的通信更为高效
  2. 切换代价:
    • 由于进程拥有独立的地址空间,进程间切换的代价较高,涉及到切换地址空间等复杂操作。
    • 线程共享进程的地址空间,线程间切换的代价较低,通常只需切换线程的上下文信息。
  3. 稳定性:
    • 进程的稳定性较高,一个进程的崩溃通常不会影响其他进程。
    • 线程的稳定性较低,一个线程的崩溃可能会影响同一进程内的其他线程。

联系

  1. 层次关系
    • 一个进程包含至少一个线程,线程共享进程的资源
  2. 通信与同步
    • 进程间通信需要较为复杂的机制,如管道、消息队列、信号量等。
    • 线程间通信更为直接,可以通过共享内存等简单的方式进行通信。
  3. 并发性
    • 两者均可以采用并发的执行方式

# 线程状态

当处于运行态的线程在执行过程中需要阻塞自己时,是否可以调度本进程的其它线程执行?还是需要阻塞整个进程?

1
2
3
4
5
6
7
在一般情况下,如果一个线程处于运行态时需要阻塞自己,操作系统可以根据调度算法继续调度本进程内的其他线程执行,而不必阻塞整个进程。

操作系统的多线程调度通常是基于线程的调度单元,而不是基于进程。每个线程都可以独立被调度和管理。当一个线程需要阻塞自己时,操作系统可以在同一进程内选择另一个就绪态的线程来执行,从而充分利用多核处理器或多处理器系统的并行性。

这种方式保持了程序的并发性,不会阻塞整个进程,只是将特定线程置于阻塞状态,而其他线程仍可以继续执行。其他线程可以是本进程内的线程,也可以是其他进程的线程,取决于系统的调度策略和就绪态线程的情况。

总的来说,线程级别的调度可以更加灵活地利用多核处理器,并提高程序的并发性和效率。

# 线程的优点

  • 在已有进程创建线程要比创建一个新进程快得多;
  • 线程的终止比进程的终止要快;
  • 同一进程的线程切换快于不同进程的进程切换;
  • 线程提高了不同执行程序之间的通信效率。大部分操作系统中,不同进程之间的通信需要内核的干预,而线程之间的通信无需内核的干预(共享存储);
  • 共享存储也引发了线程同步的问题
  • 提高了并发程度

# 线程的实现

  • 用户级线程:线程的管理工作用应用程序来完成,在用户空间内实现,内核不知道线程的存在
  • 内核级线程:线程的管理工作由内核来完成,并提供线程 API 来使用线程
  • 混合式线程:线程分为两个层次:用户层和核心层。用户层线程在用户层完成,核心层线程在操作系统内核中完成。

# 用户级线程

优点

  • 线程切换无需核心态支持,因为所有线程管理的数据结构均在用户地址空间中;
  • 线程调度独立于 CPU 调度,每个应用都可以实现自己的线程调度方法;
  • 可以运行在任何操作系统上,内核无需做任何改变。

缺点

  • 在典型的操作系统中,许多系统调用是阻塞型的,因此一个用户级线程发起的阻塞型系统调用将引起进程的所有线程被阻塞;
  • 由于内核只支持进程调度,仅将一个进程分配到一个处理器上执行,因此,同一进程的多个线程无法利用多处理器的优势。表现出来的只是多线程在一个处理器上的并发执行。

# 内核级线程

优点:

  • 同一进程的不同线程可以在多个处理器上并行执行
  • 进程中一个线程的阻塞不会引起同一进程其它线程的阻塞。
    缺点
  • 同一进程之间的线程切换需要进行模式切换,开销较大