进程调度, 一个调度器的自白

我是一个进程调度器。

我的职责是调度计算机内所有的进程,为他们分配 CPU 资源。

1. 批处理时代

想当初,操作系统创造我时,只是打算让我用 FCFS 调度算法,简单维护下进程的秩序。但我后来的发展,远远超过了他的想象。

1.1 FCFS

所谓 FCFS 就是「先来先服务(First Come First Serve)」,每个进程按进入内存的时间先后排成一队。每当 CPU 上的进程运行完毕或者阻塞,我就会选择队伍最前面的进程,带着他前往 CPU 执行。

就拿这几个进程来说吧:

按照 FCFS 算法,我就会就按 A,B,C,D,E这样的顺序来将他们送往 CPU:

这一算法听起来简单又公平,然而好景不长,我收到了一个短进程的抱怨:”上次我前面排了一个长进程,等了足足 200 秒他才运行完。我只用 1 秒就运行结束了,就因为等他,我多花了这么长时间,太不值得了。”

我仔细一想, FCFS 算法确实有这个缺陷——短进程的响应时间太长了,用户交互体验会变差。

所以我决定,更换调度算法。

1.2 SPN

这次我设计的算法叫做「短任务优先」(Shortest Process Next,SPN)。每次选择预计处理时间最短的进程。因此,在排队的时候,我会把短进程从队列里提到前面。

这一次,短进程得到了很好的照顾,进程的平均响应时间大大降低,我和操作系统都很满意。

但长进程们不干了:那些短进程天天插队,导致他们经常得不到 CPU 资源,造成了「饥饿」现象。

取消 SPN 算法的呼声越来越高。

这可是个大问题。FCFS 虽然响应时间长,但最后所有进程一定有使用 CPU 资源的机会。但 SPN 算法就不一样了,如果短进程源源不断加入队列,长进程们将永远得不到执行的机会——太可怕了。

因此,短任务优先算法需要得到改进。有什么方法既能照顾短进程,又能照顾长进程呢?

1.3 HRRN

经过和操作系统的讨论,我们决定综合考量进程的两个属性:等待时间要求服务时间——等待时间长,要求服务时间短(就是短进程)的进程更容易被选中。

为了量化,我们制定了一个公式:响应比 = (等待时间+要求服务时间)/ 要求服务时间。响应比高的算法会先执行。我们称之为「高响应比优先」(Highest Response Ratio Next,HRRN)。

这个算法得到了长短进程的一致好评。虽然我的工作量增加了(每次调度前,我都要重新计算所有等待进程的响应比)但为了进程们的公平性,这一切都是值得的。

2. 并发时代

新时代到了。

随着计算机的普及,个人用户大量增长,并发,即一次运行多个程序的需求出现了。这可难倒我了——处理器只有一个,怎么运行多个程序?

所幸 CPU 点醒了我:“我现在的运算速度既然这么快,何不发挥这项长处,弄一个「伪并行」出来?“

“伪并行?什么意思”

“就是看起来像并行,实际上还是串行。每个进程短时间交替使用我的资源,但在人类看来,这些进程就像在「同时」运行。”

我恍然大悟。

2.1 RR

经过 CPU 的提醒,我很快制定出了新的调度算法——时间片轮转算法(Round Robin,RR)。

在这个算法里,每个进程将轮流使用 CPU 资源,只不过在他们开始运行时,我会为他们打开定时器,如果定时器到时间(或者执行阻塞操作),进程将被迫「下机」,切换至下一个进程。至于下一个进程的选择嘛,直接用 FCFS 就好了。

新的算法必然会面临新的问题,现在我的问题就是,时间片的长度怎么设计?

直观来看,时间片越短,固定时间里可运行的进程就越多,可 CPU 说过,切换进程是要消耗他不少指令周期的,时间片过短会导致大量 CPU 资源浪费在切换上下文上。时间片过长,短交互指令响应会变慢。所以具体怎么取,还得看交互时间大小(感觉像没说一样,但至少给了个标准嘛)。

这一阶段,我的工作量大大提升——以前十几秒都不用切换一次程序,现在倒好,一秒钟就得切换数十次。

2.2 VRR

时间片轮转算法看起来十分公平——所有的进程时间片都是一样的。但事实真是这样吗?

I/O 密集型进程不这么认为,他对我说:“调度器大哥,时间片轮转没有照顾到我们这类进程啊!我们经常在 CPU 没呆到一半时间片,就遇到了阻塞操作,被你赶下去。而且我们在阻塞队列,往往要停留很长时间。等阻塞操作结束,我们还得在就绪队列排好长时间队。那些处理器密集型进程,使用了大部分的处理器时间,导致我们性能降低,响应时间跟不上”

考虑到这些进程的要求,我决定为他们创建一个新的辅助队列。阻塞解除的进程,将进入这个辅助队列,进行进程调度时,优先选择辅助队列里的进程。

这就是「虚拟轮转法」(Virtual Round Robin,VRR)。

从后来实际性能结果来看,这种方法确实优于轮转法。我颇为自豪。

2.3 优先级调度

有一天,操作系统忽然找到我,神神秘秘的说:“调度器啊,你是知道的,我要给整个系统提供服务,可最近用户进程太多,导致我的服务进程有时候响应跟不上。我有点担心这会给系统稳定性造成影响。”

我一听,这可是个大事,系统不稳定那还得了?调度算法得换!

既然要让操作系统的服务得到足够的运行资源,那就,干脆让他们具有最高的 CPU 使用优先权吧。

优先级调度算法就此产生了。

我向大家做出了规定——每个进程将被赋予一个优先级,自己根据自己的情况确定优先级数值,但是,用户进程的优先级不准高于内核进程的优先级。

切换程序的时候,我会从优先级 1 的队列里选择一个进程,如果优先级 1 队列为空,才会选择优先级 2 中的进程,以此类推。

当然,为了保证低优先级进程不会饥饿,我会调高等待时间长的进程的优先级。

使用这个算法,我更忙碌了,不仅需要大量切换进程,还需要动态调节优先级。可能这就是能力越大,责任越大吧。

不过我知道,正是因为我的存在,人类才能在计算机上运行多道程序——这令我感到自豪。

希望你在看完我的文章之后有所收获。

感谢你的阅读,我们后会有期!

声明:原创文章,未经授权,禁止转载

原文地址:https://www.cnblogs.com/tobe98/p/11604483.html

时间: 2024-07-30 21:44:13

进程调度, 一个调度器的自白的相关文章

【原创】(六)Linux进程调度-实时调度器

背景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: Kernel版本:4.14 ARM64处理器,Contex-A53,双核 使用工具:Source Insight 3.5, Visio 1. 概述 在Linux内核中,实时进程总是比普通进程的优先级要高,实时进程的调度是由Real Time Scheduler(RT调度器)来管理,而普通进程由CFS调度器来管理. 实

朴素的UNIX之-调度器细节

0.多进程调度的本质 我们都知道UNIX上有一个著名的nice调用,何谓nice,当然是"好"了,常规的想法是nice值越大越好,实际上,nice值越好,自己的优先级越低,那么为何不用badness呢? 事实上,如果我们理解了操作系统多进程调度系统是一个"利他"系统,这个问题就不是个问题了.nice当然还是好,不是对自己好,而是对别人好.利他系统 是一个人人为我我为人人的系统,类似还有TCP流量控制和拥塞控制,人类的宗教社会组织等等,利他系统都有一个负反馈机制,让波

Linux核心调度器之周期性调度器scheduler_tick--Linux进程的管理与调度(十八)

日期 内核版本 架构 作者 GitHub CSDN 2016-6-29 Linux-4.6 X86 & arm gatieme LinuxDeviceDrivers Linux进程管理与调度 我们前面提到linux有两种方法激活调度器:核心调度器和 一种是直接的, 比如进程打算睡眠或出于其他原因放弃CPU 另一种是通过周期性的机制, 以固定的频率运行, 不时的检测是否有必要 因而内核提供了两个调度器主调度器,周期性调度器,分别实现如上工作, 两者合在一起就组成了核心调度器(core schedu

Linux进程核心调度器之主调度器schedule--Linux进程的管理与调度(十九)【转】

转自:http://blog.csdn.net/gatieme/article/details/51872594 日期 内核版本 架构 作者 GitHub CSDN 2016-06-30 Linux-4.6 X86 & arm gatieme LinuxDeviceDrivers Linux进程管理与调度 我们前面提到linux有两种方法激活调度器:核心调度器和 一种是直接的, 比如进程打算睡眠或出于其他原因放弃CPU 另一种是通过周期性的机制, 以固定的频率运行, 不时的检测是否有必要 因而内

八、mysql视图、存储过程、函数以及时间调度器

1.create or replace view emp_view as select * from t4 ;给t4表创建一个名为emp_view的视图 2.drop view emp_view 删除视图 ======================================= 1.创建一个存储过程(查询所有数据) create procedure p1 () READS SQL DATA BEGIN select * from t4; END 2.创建一个存储过程(查询传参数据) cre

cocos2d-x调度器原理

程序运行后每达到一帧的时间间隔就会执行一次mainLoop void CCDisplayLinkDirector::mainLoop(void) { //判断是否需要释放CCDirector,通常游戏结束才会执行这个步骤 if (m_bPurgeDirecotorInNextLoop) { m_bPurgeDirecotorInNextLoop = false; purgeDirector(); } else if (! m_bInvalid) { //绘制当前场景并执行其他必要的处理 draw

Chromium网页渲染调度器(Scheduler)实现分析

在采用线程化渲染方式渲染网页时,Chromium依赖一个调度器协调Main线程和Compositor线程的执行,同时也通过这个调度器决定它们什么时候该执行什么操作.调度器将Main线程和Compositor线程的当前状态记录在一个状态机中,然后通过这个状态机决定下一个要执行的操作.这个操作在满足当前设置条件下是最优的,因此可以使网页渲染更快更流畅.本文接下来就分析Chromium网页调度器的实现. 老罗的新浪微博:http://weibo.com/shengyangluo,欢迎关注! 调度器实现

Quartz定时任务学习(四)调度器

org.quartz.Scheduler 类层次 作为一个 Quartz 用户,你要与实现了 org.quartz.Scheduler 接口的类交互.在你调用它的任何 API 之前,你需要知道如何创建一个 Scheduler 的实例.取而代之的是用了某个工厂方法来确保了构造出 Sheduler 实例并正确的得到初始化,Quartz 框架为这一目的提供了 org.quartz.SchedulerFactory 接口.角色 SchedulerFactory 就是用来产生 Scheduler 实例的.

[翻译]Go语言调度器

Go语言调度器 译序 本文翻译 Daniel Morsing 的博文 The Go scheduler,个人觉得这篇文章把Go Routine和调度器的知识讲的浅显易懂,作为一篇介绍性的文章,很不错. 译文 介绍 Go 1.1版本最大的特性之一就是一个新的调度器,由Dmitry Vyukov贡献.这个新的调度器为并行Go程序带来了令人激动.无以后继的性能提升,我觉得我应该为之写点什么东西. 这篇博客的大部分内容都已经在这篇原始设计文档中描述过了,这是一份相当好理解的文章,但是略显技术性. 虽然该