大家好,欢迎来到IT知识分享网。
即便是在多核处理器的计算机上,进程的数量通常也是远远多于处理器数量的。因此,宏观上并行运行的多个进程在微观上往往属于分时复用。进程调度的本质是怎么样让进程更好地分时复用处理器资源。概括地说,进程调度包括调度策略和进程切换两个重要话题。
针对一个处理器来说,分时复用无非就是A进程的时间配额用完以后换到B进程的事情。在这个场景下,“如何选择B进程”就是调度策略,“如何运行B进程”就是进程切换。
在讨论进程调度策略之前有几个必须明确的重要概念:一个是时间片,一个是优先级,一个是抢占调度。另外,为了使用不同的调度策略满足不同的需求,此处还对进程进行了分类。
进程调度策略的几个概念
1. 时间片
时间片即TimeSlice,指的是分时复用过程中每个进程允许持续运行的最大时间配额单位。也就是说,如果A进程持续运行了一个TimeSlice,那么它必须考虑让出CPU资源给B进程。不过有两点值得注意:
1. 进程持续运行时间可以小于TimeSlice,比如当某个进程请求的资源得不到满足时,主动睡眠(因此上文描述中强调了最大);
2. 进程持续运行时间也可以大于TimeSlice,比如当某个进程时间片用完,考虑让出CPU时并没有别的可运行进程,那么这个进程会继续运行(因此上文描述中强调了单位)。在周期性计时模式中,一般时间片是一个节拍(一个tick,即1/HZ)的整数倍,在无节拍计时模式中,时间片的长度可以更加自由。
2. 优先级
优先级即Priority,指的是在所有进程中,谁更有资格优先获得处理器资源。一般来说,现代的进程调度器都是基于优先级的调度,也就是说都是倾向于先运行优先级高的进程,再运行优先级低的进程,同优先级之间轮转调度。不过凡事皆有例外,某些情况下确实会存在高优先级进程挂起而低优先级进程运行的情况,称为“优先级倒挂”。
Linux内核中的优先级分静态优先级和动态优先级。静态优先级一般是进程创建时确定的,也可以通过特定的系统调用改变。动态优先级的初始值决定于静态优先级,但是随着进程的运行在不断地调整改变。调度策略选择进程时考虑的是动态优先级。
3. 抢占调度
抢占调度即Preemptible Scheduling,指的是一个高优先级进程是否可以强行夺取低优先级进程的处理器资源。如果可以强行夺取,就是可抢占的调度。但值得注意的是,抢占调度也并非一个简单的“是”与“否”的命题,而更多地表现为“某些时候可以抢占,某些时候不可以抢占”。因此,根据可抢占的程度,大致可以将抢占调度分为以下种类:
(1)不可抢占:完全不可抢占的调度器称之为协作式调度,A进程切换到B进程的唯一情况是A进程主动放弃。不可抢占常见于协程调度,协程是一种完全在用户态创建并管理的线程,没有内核参与。几乎不可能有完全不可抢占调度的现代操作系统内核,因为系统级的完全不可抢占是很危险的,一个简单的无限循环的应用程序就可以导致系统死机。
(2)用户态抢占:Linux-2.4以及更早的内核的调度器只允许用户态抢占。指的是当某个进程运行在用户态时,一个更高优先级的进程可以抢占该进程的时间片。然而,内核不可能随时随地检测是否有更高优先级的进程产生,而是在特定的时间点检测,这些时间点不妨称之为检查点(CheckPoint)。对于用户态抢占,其检查点是“异常、中断、或者系统调用处理完成后返回用户态的时候”。允许用户态抢占是现代操作系统内核设计的底线,Linux内核配置选项中的“不抢占”(CONFIG_PREEMPT_NONE)实际上指的是用户态抢占。
(3)内核态抢占:Linux-2.6以及更新的内核的调度器允许内核态抢占。指的是某个进程不管是运行在用户态还是内核态,一个更高优先级的进程都可以抢占该进程的时间片。同样,随时随地的检查是不可能的,允许内核态抢占无非是在用户态抢占的基础上,又增加了更多的抢占检查点而已。一般来说,内核态抢占的检查点是“异常、中断、或者系统调用处理完成后返回的时候”。请注意,异常中断或系统调用处理完成后,既可以返回到内核态,也可以返回到用户态,因此内核态抢占是用户态抢占的超集。
Linux的内核态抢占还可以细分成以下三种:
l 内核态自愿抢占:即内核配置选项中的CONFIG_PREEMPT_VOLUNTARY。采用白名单机制,在用户态基础上增加了一些特定的内核态检查点,通过显式调用might_resched()来决定要不要调度到更高优先级的进程。应该说这不算严格意义上的内核态抢占。
l 内核态完全抢占:即内核配置选项中的CONFIG_PREEMPT。采用黑名单机制,除非是在临界区里面,其他任意时刻都允许内核态抢占。临界区指的是关中断的区间(包括硬中断和软中断)以及用preempt_disable()和preempt_enable()所包含的区间。Linux内核态抢占通常指的是这一种。
l 内核态实时抢占:即内核配置选项中的CONFIG_PREEMPT_RT。这是最彻底的内核态抢占,取消了所有临界区,即便是在中断处理过程中也允许抢占。标准的Linux内核从5.3版本开始加入了这种模式初步支持,但到目前为止PREEMPT_RT尚不完整(需要更多的增强支持)。
内核抢占程度越高,对交互式应用的响应性越好;但它带来了更多的进程切换代价,对吞吐率有较大的影响。因此,一般桌面系统建议启用内核态抢占,而服务器系统建议关闭内核态抢占。
所有的调度器都围绕着这三个基本概念进行设计,调度器之间的区别,无非是时间片的长短定义不一样,优先级的计算以及围绕优先级对进程的组织不一样,以及允许抢占的程度不一样。
进程的分类
从调度器的角度来看,进程可以分成三大类:
1.交互式进程:此类进程有大量的人机交互,因此进程不断地处于睡眠状态,等待用户输入。典型的应用比如编辑器。此类进程对系统响应时间要求比较高,否则用户会感觉系统反应迟缓。
2.批处理进程:此类进程不需要人机交互,在后台运行,需要占用大量的系统资源。但是能够忍受响应延迟。比如编译器和各种服务器程序。
3.实时进程:实时对调度延迟的要求最高(有最后期限即Deadline的约束),这些进程往往执行非常重要的操作,要求立即响应并执行。比如视频播放软件(软实时)或飞机飞行控制系统(硬实时)。很明显这类程序不能容忍长时间的调度延迟(超过Deadline约束),软实时进程超过Deadline会影响用户体验,而硬实时进程超过Deadline会导致灾难。
在Linux内核中,交互式进程和批处理进程统称为普通进程(与实时进程相对)。内核对实时进程和普通进程采用了不同的调度策略,前者相对简单,自Linux问世以来没有本质的变化(主要是先进先出算法FIFO和时间片轮转算法Round-Robin,基于优先级调度。前者在相同优先级的进程之间使用先进先出,后者在相同优先级的进程之间使用轮转调度);后者更为复杂,需要兼顾交互式进程和批处理进程的需求。
以上部分内容来源于《用“芯”探核 基于龙芯的Linux内核探索解析》,陈华才著。
相关阅读
《用“芯”探核 基于龙芯的Linux内核探索解析》
作者:陈华才
这是一本基于龙芯平台,结合Linux-5.4.x 版本的内核源代码来解析Linux 内核的书籍。
本书首先介绍了有关龙芯处理器和Linux 内核的基础知识,然后重点讲解了内核启动、异常与中断、内存管理、进程管理、显卡驱动、网卡驱动和电源管理这7 大板块的内容。本书甚少涉及代码的细枝末节,而是重点关注代码实现的主干流程,并且创造性地引入了树形视图和链式视图这两种比流程图更好用的代码解析方法。
本书适合Linux 系统相关的开发人员,特别是基于龙芯处理器做内核开发的技术人员学习参考。
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/51409.html