为了提高CPU的利用率发明了进程
处理器的速度比大部分IO设备的速度都要快得多。
程序在IO设备上运行时,处理器必须暂停保持空闲,等待IO处理结果(如图a)。这将导致严重的资源浪费。
如果当CPU阻塞,等待其他设备运行结果时,将原有程序挂起,转而运行其他程序(如图b),则CPU的利用率将会得到大幅提高。
另一方面,在单核系统中,为了在宏观上实现程序的并行,避免一个程序长时间占用CPU导致其他程序无法被执行,在一定时间后,CPU主动将正在运行的程序调出,转而执行其他程序。
尽管在微观上,仍然是一条条指令顺序执行,但在宏观上实现了并行。
为了恢复执行时,程序能在被挂起前的位置继续执行接下来的代码,需要将程序的上下文及有关数据保存起来,存储在内存中,将这部分信息称之为进程。
进程是内存中存储的数据结构
进程是系统作为资源分配的基本单位,由一组机器指令、数据结构和堆栈等组成,能独立运行的活动实体。
可以把进程视为由一组元素组成的实体,包括程序代码和与之相关联的数据集。
在内存中,分配一个称为**进程控制块(Process Control Block,PCB)**的数据结构,用于存放进程的信息。
操作系统在执行调度、资源分配、中断处理、性能监控和分析时,都能读取和修改PCB。可以说PCB集合定义了操作系统的状态。
PCB包含进程标识信息、进程状态信息和进程控制信息。
进程标识信息
-
标识符:
与进程相关的唯一标识符。每个进程都分配了一个唯一的数字标识,可以简单的表示为主进程表中的一个索引。
进程之间通信时,进程标识可用于指明通信目标;进程自主创建其他进程时,标识符用于指明每个进程的父进程和子进程。标识符包括:
- 进程的标识符(Process ID, PID);
- 创建该进程的进程(父进程)的标识符;
- 用户标识符(User ID,UID);
进程状态信息
运行一个进程时,进程的信息会出现在寄存器中。中断进程时,必须保存该寄存器的所以信息,以便腾出寄存器给其他进程使用,
并且恢复执行时可以恢复这些信息到寄存器中。包括:
-
用户可见的寄存器:
用户可见的寄存器是处理器在用户模式下,执行机器语言时可以访问的寄存器。
-
控制和状态寄存器:
用于控制处理器操作的寄存器,包括:
- 程序计数器:包涵下一条待取指令的地址;
- 条件码:最近算术或逻辑运算的结果(如符号、零、进位、等于、溢出等);
- 状态信息:包括终端允许/禁用标志、执行模式;
-
栈指针:
栈用于保存参数和过程调用或系统调用的地址。
进程控制信息
操作系统在控制和协调各种活动进程所需的额外信息。包括:
-
调度和状态信息:
操作系统执行调度所需的信息。包括:
- 进程状态:定义待调度执行的进程的准备情况;
- 优先级:描述进程调度的优先级;
- 调度相关信息:取决于调度算法,如进程的等待时间、上次执行时间等;
- 事件:进程在继续执行前,等待的事件标识;
-
数据结构:
进程可以以队列、环或其他结构连接到其他进程。
-
进程间通信:
各种标记、信号和信息可以与两个无关进程间通信关联。
-
进程特权:
进程根据其可以访问的内存和可执行的指令类型来赋予特权。
-
存储管理:
包括一些指针,指向分配给该进程的虚存的段表或页表。
-
资源所有权和使用情况:
指示进程控制的资源。
操作系统通过创建和管理进程控制块来管理进程。PCB包含了充分的信息,也因此可以中断一个进程的执行,并在之后恢复。
PCB分为五种状态用来描述进程的行为
在任何一个时刻,一个进程要么正在被执行,要么未执行。
未被执行的进程,一些进程已经就绪,待CPU调度便可马上执行;一些进程被阻塞等待IO;还有的进程已经运行结束,需要辅助程序分析和记录;
五状态模型对进程从新建到退出的各个状态进行了划分:
- **新建态:**进程已经创建,但还未加入可执行进程组。
- **就绪态:**进程已经准备好,一旦被调度,便可立即执行。
- **运行态:**进程正在CPU中执行。
- **阻塞态:**进程在某些事件发生前不执行。如:等待I/O返回结果。
- **退出态:**进程执行结束,或某种原因被取消,从可执行进程组中释放。待分析程序收集完有关数据后,操作系统便删除该进程的所有信息。
进程状态之间的转换
-
空 -> 新建:
操作系统已经执行力创建进程的必要动作,但还为执行。可能是内存不足,或系统总进程数已满。
在新建态时,操作系统所需的关旭该进程的信息保存在内存的进程表中,但PCB本身还没有被载入内存。 -
新建 -> 就绪:
操作系统准备好接纳一个新进程。
-
就绪 -> 运行:
CPU调度器根据调度策略,选取就绪队列中的进程,进入CPU执行。
-
运行 -> 退出:
若当前正运行的进程已完成或取消,将被操作系统终止。
-
运行 -> 就绪:
正在运行的进程已经达到允许不中断执行的最大时间段。或被有更高优先级的进程所抢占。
-
运行 -> 阻塞:
进程请求其他资源时,必须等待事件的响应才能继续执行,则进入阻塞态。
-
阻塞 -> 就绪:
所等待的事件发生时,被阻塞的进程转换到就绪态,等待调度执行。
-
就绪 -> 退出:
当父进程主动结束子进程,或父进程终止,其相关的所有子进程都将被终止。
-
阻塞 -> 退出:
与上一项一致。
进程在发生系统中断或陷阱时切换线程
-
普通中断(Interrupt):
控制权首先转给中断处理器,完成一些基本辅助工作后,再转给特定中断的操作例程。
- 时钟中断: 当前进程的执行时间已经超过最大允许时间段(时间片,time slice),需要切换到就绪态,并调度另一个进程执行。
- I/O中断: 若发生的I/O响应,是一个或多个进程正在等待的事件,则把所有处于阻塞态的进程转换为就绪态(操作系统可能会根据调度算法继续执行运行态的进程)。
- 内存失效: 处理器遇到一个引用虚存地址时,必须发起I/O请求,从外存中将内存块调入内存,进程进入阻塞态。
-
陷进(trap):
操作系统若判定为致命时,将当前进程置为退出态;否则根据错误的性质,可能尝试恢复程序或通知用户。
-
系统调用:
使用系统调用时,会将用户进程置为阻塞态。
新增挂起态将进程换出内存
进程由新建态转换为就绪态时,便将该进程有关的代码和数据保存在内存中。在未使用虚拟内存的系统中,内存不足时,如果没有就绪的进程,那么无法将新建态的进程转换为就绪态。导致CPU的利用率降低。
从操作系统的角度,可以将内存中的阻塞或就绪的一部分或全部进程转移到磁盘中。
线程
在进程中,将可被操作系统调度和分派的实体,独立出来,称之为线程。未提出线程概念的系统中,可认为是一个单线程进程模型。
一个进程中可能有一个或多个线程,每个线程都有:
- 执行状态;
- 未运行时保存的线程上下文;
- 执行栈;
- 每个线程用于局部变量的一些静态存储空间;
- 与进程内其他线程共享的内存和资源的访问。
多线程共享进程的状态和资源
多线程模型中,每个线程会有单独的栈和单独的控制块,控制块中包含寄存器值、优先级和其他与线程相关的状态信息。
因此,进程中的所有线程共享该进程的状态和资源,所有线程都驻留在同一块地址空间中,并可访问相同的数据。
当某个线程改变了内存中的一个数据项时,其他线程在访问这一数据项时会看到这一变化。
若一个线程以读权限打开一个文件,那么其他线程也能从这个文件中读取数据。
因此需要同步各种线程的活动,以便他们互不干扰且不破坏数据结构。
多线程相比单线程性能更高
-
在已有进程中创建一个新线程的时间,远少于创建一个全新进程的时间。
线程创建要比在Unix中创建进程快 倍。
-
终止线程要比终止进程所花的时间少。
-
同一进程内,线程切换的时间,少于进程间切换的时间。
-
同一进程内的线程无需调用内核便可通过共享内存和文件进行通信。
挂起态对象成没有意义
如果一个进程被换出,由于所有线程共享该进程的地址空间,因此所有线程都将被换出。
线程仅有4种状态:
-
派生:
进程中的线程可在同一进程中派生另一个线程。新线程拥有自己的寄存器上下文和栈空间,并放入就绪队列中。
-
阻塞:
线程需要等待一个事件时,会被阻塞,转而执行另一个就绪的线程。
-
解除阻塞:
发生阻塞一个线程的事件时,会将该线程转移到就绪队列中。
-
结束:
一个线程完成后,会释放其寄存器上下文和栈。
线程阻塞不会导致进程阻塞
若一个被阻塞线程阻塞了整个进程,则会丧失线程的某些灵活性和能力。