操作系统中,每时每刻都有着许许多多的进程在执行着,即便是现在最为强大的多核心 CPU,同时能够执行的任务数量也是相当有限的,那么,在这样资源有限的场景下,这么多进程如何来调度,哪些进程更重要哪些进程的执行可以稍微暂缓呢?这就是操作系统调度器的工作。本文我们就来详细介绍一下。
众所周知,根据进程的运行状态,进程可以被划分为两类:
我们常见的与用户发生交互的程序一般都是 IO 密集型进程,这类进程很少占用 CPU,大部分时间在等待着用户进行操作或者 IO 操作完成,但一旦用户进行了操作,CPU 就必须立即响应,否则就会直接影响到用户的体验,想象一下,你移动了一下鼠标,CPU 由于被 CPU 密集型进程占用着,而让你的鼠标在屏幕上一顿一顿地移动,这显然太过于糟糕。所以 IO 密集型进程的优先级却要高于 CPU 密集型进程。
而非交互式进程通常是需要密集计算的 CPU 密集型进程,这类进程由于不与用户交互,从而在用户无感知的情况下运行,对响应时间的要求也就没有 IO 密集型进程那么高,所以在操作系统中,他们就属于低优先级进程。
在操作系统中,同时运行着那么多进程,操作系统是如何确定每个进程的优先级呢?
在 Linux 操作系统中,系统会为每个进程打一个分,这个分就是 PR 值,它是 Priority 的前两个字母。
通过 PR 值的范围,linux 换分出了两类进程:
但有时,用户可能会不认可操作系统的优先级数值,而是想要去手动调整进程的优先级。此时,如果让用户直接干预 PR 值,那风险就显得很大。Linux 为用户层设计了一个 Nice 值,翻译为“谦让值”。
PR = PR + Nice
Nice 值取值为 -20 到 19,它的存在让用户得以在一定范围内对 PR 值进行调整。
由于用户能够干预的一般都是普通进程,因此,Nice 值为 0 表示不干预,负值表示增加优先级,正值表示降低优先级。
在调度进程时,操作系统有两种选择:
显然,协作式调度的方式下,执行中的进程一旦想要让出 CPU,它必须自己去保存自己的工作状态,而操作系统所需要做的仅仅是在一个任务让出 CPU 后决定让谁来接替它,这样的设计方式简单而高效,但缺陷也显而易见,一旦一个不那么重要的进程长期霸占 CPU,其他进程都将得不到执行。
而抢占式调度的模式下,操作系统尽管增加了进程切换的开销以及调度算法设计的复杂度,但却可以更加灵活地分配 CPU 的时间资源,所以常见的操作系统一般都采用抢占式调度的策略。
调度器设计中需要考虑两个重要指标:
可以设想,众多等待执行的进程就像是在超市中等待结账的顾客,对他们而言,“响应时间”相当于排队的时间,“周转时间”相当于从开始排队完成结账的时间。对于这些顾客而言,缩短周转时间是他们的核心诉求,但对于超市来说,综合考虑不同顾客对于排队时长的特殊诉求,合理安排所有顾客的排队顺序,才能够降低系统周转时间,拥有良好的用户体验。
综合来说,操作系统的调度原则是:
本文,我们从操作系统的整体层面,了解了操作系统进程调度的基本概念和设计思想,但我们尚未触及核心部分,到底 linux 系统中的调度器是如何设计的,又有着怎样的历史沿革,出现了哪些算法?它们之间又有着哪些优劣?敬请期待下一篇文章的详细讲解。