进程、线程及进程调度算法

进程

程序执行模型:
CPU + I/O
进程:进程是一个正在运行的程序的实例(A program in execution),是 CPU 资源分配和调度的最小单元
进程包含的维度:
a. 所处理的数据
b. 所运行的程序
c. 所处于的状态(PCB)
进程的状态:
(1) 运行态:正在 CPU 上运行的状态
(2) 就绪态:已就绪但没有被调度程序选中
(3) 阻塞态:进程在等待资源的状态
(4) 创建态:操作系统为进程分配资源,初始化PCB
(5) 终止态:操作系统回收进程所拥有的资源,撤销PCB
状态之间的转换关系如下:
运行态 → 就绪态 → 阻塞态
运行态 ←→ 阻塞态
进程控制块(Process Control Block)
一个进程对应一个 PCB
操作系统的内存管理:
就是让进程在不同队列之间切换
作业队列:希望执行但还没有执行的进程组成的队列
就绪队列:所有就绪状态的进程都放在一个就绪队列
等待队列:每种资源对应一个队列,所以会有多个等待队列
进程的实现
操作系统内核维护一个进程表,也称为进程控制块(PCB)。进程表项为一个进程启动的上下文信息,包括进程管理(寄存器,PC,PSW,调度信息,打开文件的状态等)、存储管理(代码段、数据段、堆栈段指针等)、文件管理(目录、PID等)信息,进程切换运行态时会把上下文信息读取或写入 PCB。
进程操作:
创建子进程:分配 PCB 号:分配资源;
分配资源的两种方式:
a. 复制父进程的所有资源
b. 等待操作系统分配资源


线程

线程是 CPU 资源调度的最小单元
线程相对于进程的优势:
a. 响应快
b. 资源共享
c. 节约内存

引入线程机制之后的变化:

1. 进程是分配资源的基本单位,线程是调度的基本单位
2. 引入线程后,各线程间也能并发,提升了并发度
3. 线程间并发不需要切换上下文,系统开销小

线程的实现方式

1. 用户级线程 (用户管理)
2. 内核级线程 (操作系统内核管理)
3. 组合实现方式
操作系统只看得见内核级线程,因此只有 内核级线程 才是处理器分配的单位

多线程模型

1. 多对一模型(用户级线程)
优点:进程管理开销小,效率高
缺点:并发度低(一个线程阻塞会导致整个进程阻塞)
2. 一对一模型(内核级线程)
优点:并发度高
缺点:进程管理开销大
3. 多对多模型(组合实现)
优点:并发度高、进程管理开销小


CPU 的调度算法

CPU 调度器的使命:
从内存中就绪队列中,选取一个进程;将 CPU 资源分配给该进程

调度算法:

FCFS(First Come First Serve)
先到先算

SJF(Shortest Job First)
前提:进入就绪队列时进程“预告”需要多长的 CPU 运行时间
先计算需要计算时间最少的进程

SRTF(Shortest Remaining Time First)
抢占式的 SJF 算法

SJF 是最优的算法,但是有致命缺陷:无法预测需要的 CPU 运行时间(原因:程序中太多 if-else 语句,分支很多,而且出现 scanf 类似的输入操作,无法预估所需时间),而且会导致“饥饿”(长作业一直得不到调度)

HRRN(Highest Response Rate Next)
高响应比算法
每一轮计算每一个进程的响应比
响应比 = \frac {等待时间 + 运行时间} {运行时间}
该算法可以防止“饥饿”
相当于折中了 FCFS 和 SJF 算法

现实中常用的调度算法:

PS(Priority Scheduling)优先级算法
优先级最高的进程最先被 CPU 调度
缺陷:进程饥饿,优先级低的进程可能永远也不会被 CPU 调度
解决方案:Aging思想,就绪队列中的进程随时间的推移,优先级单调递增(动态优先级)

RR(Round Robin)轮转算法
思路:每一个进程每一次使用 CPU 时都会有一个时间上限(时间片),超过上限时该进程自动回到就绪队列队尾,等待被调用
效果:每一个进程需要等待的响应时间不超过 (n - 1) t
(该算法只注重响应时间,换句话说,如果时间片足够大,轮转算法将退化为先来先服务算法)
解决问题:防止某些进程很耗时间,导致整体响应时间太长
存在问题:上下文切换会造成额外开销

MQ(Multilevel Queue)多级队列算法
把等待不同资源的进程放入不同的就绪队列,对于每一个队列根据情况采取合适的调度算法

多级反馈队列.png

MFQ(Multilevel Feedback Queue)多级反馈队列算法
MQ加强版,进程在不同的就绪队列之间可以进行切换(默认为抢占式)

推荐阅读更多精彩内容