# 调度,第 1 部分:调度过程
> 原文:<https://github.com/angrave/SystemProgramming/wiki/Scheduling%2C-Part-1%3A-Scheduling-Processes>
## 关于调度的思考。
[CPU 调度](https://en.wikipedia.org/wiki/Scheduling_(computing))是有效选择在系统 CPU 内核上运行哪个进程的问题。在繁忙的系统中,将有比 CPU 内核更多的可立即运行的进程,因此系统内核必须评估应该在 CPU 上运行哪些进程以及哪些进程应放在就绪队列中以便稍后执行。
多线程和多 CPU 内核的额外复杂性被认为是对这个初始展示的干扰,因此在此忽略。
非母语人士的另一个问题是“时间”的双重含义:“时间”一词可用于时钟和经过时间的上下文。例如“第一个过程的到达时间是上午 9 点。”并且,“算法的运行时间是 3 秒”。
## 如何测量调度以及哪种调度程序最佳?
调度会影响系统的性能,特别是系统的 _ 延迟 _ 和 _ 吞吐量 _。吞吐量可以通过系统值来度量,例如 I / O 吞吐量 - 每秒写入的字节数,或者每单位时间可以完成的小进程数,或者使用更高级别的抽象,例如客户数量每分钟处理的记录。延迟可以通过响应时间(进程可以开始发送响应之前的经过时间)或等待时间或周转时间(完成任务所经过的时间)来度量。不同的调度程序提供不同的优化权衡,可能适合或不适合所需的使用 - 没有针对所有可能的环境和目标的最佳调度程序。例如,“shortest-job-first”将最小化所有作业的总等待时间,但在交互式(UI)环境中,最好将响应时间最小化(以某些吞吐量为代价),而 FCFS 看起来直观公平且易于实现但是遭遇了康宏效应。
## 什么是到达时间?
进程首次到达就绪队列并准备开始执行的时间。如果 CPU 空闲,则到达时间也将是执行的开始时间。
## 什么是先发制人?
在没有抢占的情况下,进程将一直运行,直到它们无法再进一步利用 CPU。例如,以下条件将从 CPU 中删除进程,并且可以为其他进程调度 CPU:进程由于信号而终止,被阻塞等待并发原语或正常退出。因此,一旦调度了一个进程,即使具有高优先级的另一个进程(例如,较短的作业)出现在就绪队列上,它也将继续。
通过抢占,如果将更优选的进程添加到就绪队列,则可以立即移除现有进程。例如,假设在 t = 0 时使用 Shortest Job First 调度程序,有两个进程(P1 P2),执行时间为 10 和 20 ms。 P1 已预定。 P1 立即创建一个新进程 P3,执行时间为 5 毫秒,并将其添加到就绪队列中。没有先发制人,P3 将在 10ms 后运行(P1 完成后)。通过抢占,P1 将立即从 CPU 中逐出,而是放回到就绪队列中,而 P3 将由 CPU 执行。
## 哪些调度员遭受饥饿?
任何使用优先级形式的调度程序都可能导致饥饿,因为可能永远不会调度早期进程(分配 CPU)。例如,对于 SJF,如果系统继续有许多要安排的短作业,则可能永远不会安排更长的作业。这一切都取决于[类型的调度程序](https://en.wikipedia.org/wiki/Scheduling_(computing)#Types_of_operating_system_schedulers)。
## 为什么可以将一个进程(或线程)放在就绪队列上?
当一个进程能够使用 CPU 时,该进程被置于就绪队列中。一些例子包括:
* 阻止进程等待存储或套接字中的`read`完成,现在可以使用数据。
* 已创建新进程并准备启动。
* 进程线程在同步原语(条件变量,信号量,互斥锁)上被阻止,但现在能够继续。
* 阻止进程等待系统调用完成,但已传递信号并且信号处理程序需要运行。
在考虑线程时可以生成类似的示例。
## 效率措施
`start_time`是进程的挂钟开始时间(CPU 开始工作)`end_time`是进程的结束挂钟(CPU 完成进程)`run_time`是所需的 CPU 时间总量`arrival_time`是进程进入调度程序的时间(CPU 可能无法启动它)
## 什么是'周转时间'?
从进程到达到结束的总时间。
`turnaround_time = end_time - arrival_time`
## 什么是'响应时间'?
从进程到达到 CPU 实际开始工作所花费的总延迟(时间)。
`response_time = start_time - arrival_time`
## 什么是“等待时间”?
等待时间是 _ 总 _ 等待时间,即进程在就绪队列上的总时间。一个常见的错误是认为它只是就绪队列中的初始等待时间。
如果没有 I / O 的 CPU 密集型进程需要 7 分钟的 CPU 时间才能完成,但需要 9 分钟的挂钟时间才能完成,我们可以得出结论,它已被放置在就绪队列中 2 分钟。对于那些 2 分钟,该过程已准备好运行但没有分配 CPU。工作等待时无关紧要,等待时间为 2 分钟。
`wait_time = (end_time - arrival_time) - run_time`
## 什么是车队效应?
“Convoy 效应是持续备份 I / O 密集型进程的地方,等待占用 CPU 的 CPU 密集型进程。这导致 I / O 性能不佳,即使对于 CPU 需求很小的进程也是如此。”
假设 CPU 当前已分配给 CPU 密集型任务,并且存在一组处于就绪队列中的 I / O 密集型进程。这些进程只需要很少的 CPU 时间,但它们无法继续,因为它们正在等待从处理器中删除 CPU 密集型任务。在 CPU 绑定进程释放 CPU 之前,这些进程一直处于饥饿状态。但很少会释放 CPU(例如,在 FCFS 调度程序的情况下,我们必须等到进程因 I / O 请求而被阻止)。 I / O 密集型进程现在可以最终满足他们的 CPU 需求,他们可以快速完成这些需求,因为他们的 CPU 需求很小,并且 CPU 再次被分配回 CPU 密集型进程。因此,整个系统的 I / O 性能受到所有进程的 CPU 需求饥饿的间接影响。
这种效果通常在 FCFS 调度程序的上下文中讨论,但循环调度程序也可以展示长时间量子的康宏效应。
## Linux 调度
截至 2016 年 2 月,Linux 默认使用 _ 完全公平调度程序 _ 进行 CPU 调度,使用预算公平调度“BFQ”进行 I / O 调度。适当的调度会对吞吐量和延迟产生重大影响。延迟对于交互式和软实时应用(如音频和视频流)尤为重要。有关详细信息,请参阅此处的讨论和比较基准[ [https://lkml.org/lkml/2014/5/27/314](https://lkml.org/lkml/2014/5/27/314) ]。
以下是 CFS 的日程安排
* CPU 创建一个红黑树,其中包含进程虚拟运行时(runtime / nice_value)和睡眠公平性(如果进程正在等待某些内容,则在等待时将其提供给 CPU)。
* (好的值是内核优先处理某些进程的方式,优先级越低)
* 内核根据此度量标准选择最低的一个,并安排该进程下一次运行,将其从队列中取出。由于红黑树是自平衡的,因此保证$ O(log(n))$(选择 min 进程是相同的运行时)
虽然它被称为公平调度程序,但存在一些问题。
* 调度的进程组可能具有不平衡负载,因此调度程序粗略地分配负载。当另一个 CPU 获得空闲时,它只能查看组计划的平均负载而不是单个核心。因此,只要平均值很好,空闲 CPU 就不会从正在燃烧的 CPU 中获取工作。
* 如果一组进程正在运行,则在非相邻核心上存在错误。如果两个核心超过一跳,负载平衡算法甚至不会考虑该核心。这意味着如果 CPU 是空闲的并且正在做更多工作的 CPU 超过一跳,它将不会进行工作(可能已经修补)。
* 线程在一个核心子集上进入休眠状态后,当它被唤醒时,它只能在它正在睡眠的核心上进行调度。如果这些核心现在是公共汽车
- UIUC CS241 系统编程中文讲义
- 0. 简介
- #Informal 词汇表
- #Piazza:何时以及如何寻求帮助
- 编程技巧,第 1 部分
- 系统编程短篇小说和歌曲
- 1.学习 C
- C 编程,第 1 部分:简介
- C 编程,第 2 部分:文本输入和输出
- C 编程,第 3 部分:常见问题
- C 编程,第 4 部分:字符串和结构
- C 编程,第 5 部分:调试
- C 编程,复习题
- 2.进程
- 进程,第 1 部分:简介
- 分叉,第 1 部分:简介
- 分叉,第 2 部分:Fork,Exec,等等
- 进程控制,第 1 部分:使用信号等待宏
- 进程复习题
- 3.内存和分配器
- 内存,第 1 部分:堆内存简介
- 内存,第 2 部分:实现内存分配器
- 内存,第 3 部分:粉碎堆栈示例
- 内存复习题
- 4.介绍 Pthreads
- Pthreads,第 1 部分:简介
- Pthreads,第 2 部分:实践中的用法
- Pthreads,第 3 部分:并行问题(奖金)
- Pthread 复习题
- 5.同步
- 同步,第 1 部分:互斥锁
- 同步,第 2 部分:计算信号量
- 同步,第 3 部分:使用互斥锁和信号量
- 同步,第 4 部分:临界区问题
- 同步,第 5 部分:条件变量
- 同步,第 6 部分:实现障碍
- 同步,第 7 部分:读者编写器问题
- 同步,第 8 部分:环形缓冲区示例
- 同步复习题
- 6.死锁
- 死锁,第 1 部分:资源分配图
- 死锁,第 2 部分:死锁条件
- 死锁,第 3 部分:餐饮哲学家
- 死锁复习题
- 7.进程间通信&amp;调度
- 虚拟内存,第 1 部分:虚拟内存简介
- 管道,第 1 部分:管道介绍
- 管道,第 2 部分:管道编程秘密
- 文件,第 1 部分:使用文件
- 调度,第 1 部分:调度过程
- 调度,第 2 部分:调度过程:算法
- IPC 复习题
- 8.网络
- POSIX,第 1 部分:错误处理
- 网络,第 1 部分:简介
- 网络,第 2 部分:使用 getaddrinfo
- 网络,第 3 部分:构建一个简单的 TCP 客户端
- 网络,第 4 部分:构建一个简单的 TCP 服务器
- 网络,第 5 部分:关闭端口,重用端口和其他技巧
- 网络,第 6 部分:创建 UDP 服务器
- 网络,第 7 部分:非阻塞 I O,select()和 epoll
- RPC,第 1 部分:远程过程调用简介
- 网络复习题
- 9.文件系统
- 文件系统,第 1 部分:简介
- 文件系统,第 2 部分:文件是 inode(其他一切只是数据...)
- 文件系统,第 3 部分:权限
- 文件系统,第 4 部分:使用目录
- 文件系统,第 5 部分:虚拟文件系统
- 文件系统,第 6 部分:内存映射文件和共享内存
- 文件系统,第 7 部分:可扩展且可靠的文件系统
- 文件系统,第 8 部分:从 Android 设备中删除预装的恶意软件
- 文件系统,第 9 部分:磁盘块示例
- 文件系统复习题
- 10.信号
- 过程控制,第 1 部分:使用信号等待宏
- 信号,第 2 部分:待处理的信号和信号掩码
- 信号,第 3 部分:提高信号
- 信号,第 4 部分:信号
- 信号复习题
- 考试练习题
- 考试主题
- C 编程:复习题
- 多线程编程:复习题
- 同步概念:复习题
- 记忆:复习题
- 管道:复习题
- 文件系统:复习题
- 网络:复习题
- 信号:复习题
- 系统编程笑话