经常做linux下纯应用软件的开发,只知道linux这种非实时性操作系统内核是抢占式调度,但对其原理却不明所以,故本节做简要学习记录。最终的目的是让自己的进程如何被实时调度而不被其他进程抢占,即打破所谓的“时间片”的限制!
一、多任务操作系统
不管计算机是单核还是多核,操作系统一般至少也有上百个进程需要同时跑,但同一时间点一个核上只能跑一个进程,其他的进程都会被阻塞。但要实现所有进程都在跑的“假象”,只能让进程轮流跑,即每个进程跑一会再让给其他进程,每个进程能跑的时间片段,就是我们常说的“时间片”。当进程的时间片用完了,就会发生“抢占”。
所谓“抢占”,是指用完时间片的当前进程被强制挂起,然后从就绪进程队列里取出排队的进程运行,即新进程抢占原来的进程或原来的进程被抢占。
原来的进程被挂起,就要对他的运行现场进行保存,以便他恢复时继续运行,这里的保存需要cpu的开销,并且重新运行时cache也要重新加载,所以这里的开销相对较大。
这种分配时间片,进程挂起和选取新的进程运行的工作是调度程序负责的。所以调度程序数多任务操作系统的基础,它的目的就是充分利用系统的资源,让系统达到最大的“吞吐量”。
开头我们所说的我们的最终目的是让我们自己的进程实时运行,完全占有cpu核心,而不被调度走,显然与这种调度机制是相悖的!
二、完全公平调度算法CFS
比如只有两个进程A、B,运行周期是20ms,则每个进程运行时间是10ms。
但其实进程是有优先级的,优先级影响的是时间片,如果进程A拥有更高的优先级,则可能是A运行15ms,B运行5ms。
nice值能够反映进程的优先级.
可以通过top或ps -elf命令查看,图中NI列就是nice值:
nice值的取值范围是[-20—+19],数值越小优先级越高,普通进程创建后其值为0。
nice值只是影响分配时间片的权重,而不是直接映射到时间片。实际上系统的时间片不是一个固定的值,是系统根据负载进行计算出来的一个合理值。
CFS所谓的公平性。假如时间片是固定的一个值,则n个相同优先级的进程同比例占有时间片,那如果n足够大,每个进程可执行的时间就相当短,而进程切换消耗极大,会严重影响性能。所以时间片的值不是写死的,另外CFS保证进程最小的执行时间为1ms,即使进程再多,再频与切换,每个进程也至少获得1ms的运行时间。所以在cpu核数不够,而进程又很多的情况下,其实这并不是一个完美的调度算法,假如是单核1000的个进程,也就是说最坏的情况下第1000个进程在第1000ms时才被运行。但目前我们用的都是多核,且一般只有上百个进程,所以这种调度算法依然表现良好。
另一点进程分为IO消耗型和处理器消耗型,CFS调度算法能很好的应对这种分类。
IO消耗型的进程,大部分时间处于阻塞等到IO的返回,如读取文件、读取键盘\界面操作等;处理器消耗型把大把时间用在处理数据上。
IO消耗型要经常处于可运行状态因为它要及时处理IO请求,但只会运行一小会,因为它马上就会进行阻塞,等到IO请求。而处理器消耗型希望一直运行以处理数据。如果他俩优先级相同,按照之前的理解他俩应该均分时间片,即对cpu的占有率各位50%,但假如处理器消耗型的时间片用完了,切换到IO消耗型的进程,此进程其实大概率依然在阻塞而立即释放cpu,切换回处理器消耗型,这样就是白白进行了两次切换。CFS的做法是让处理器消耗型的进程一直执行,直到IO消耗型的进行来进行“抢占”,此时IO消耗型的进程被唤醒后可以立即抢占处理器消耗型的进程,这样也能让用户输入得到及时返回。所以处理器消耗型进程的cpu占有率实际上是>50%的,但一旦IO消耗型可运行时可以进行抢占,这样既能尽量保证交互式应用的性能,又能最大利用cpu。
可见CFS的公平并不是平等,而是以资源最大利用率为标准。
三、调度策略
linux内核是以模块方式加载的,各模块互不影响,所以内核允许各种模块共存(这种模块加载方式主要用于内核裁剪,当编译内核时会有加载的模块选项,我们可以只选择我们需要的模块,这样就能编译一个最小的内核)。
linux内核提供给我们三种调度策略:SCHED_FIFO、SCHED_RR、SCHED_NORMAL。前两种是实时调度策略,后一种是非实时调度策略。实时调度策略并不被我们上文介绍的完全公平调度器管理,而是被一个特殊的实时调度器管理。所以我们平时所写的进程的调度策略默认都是SCHED_NORMAL。
开篇我们说到我们的目的是要让我们的进程实时运行,所以我们的进程是否应该用像SCHED_FIFO这样的调度策略呢?
顾名思义SCHED_FIFO是一个先进先出的调度算法。处于可运行状态的SCHED_FIFO进程会比其他任何SCHED_NORMAL级的进程先被调度。而且一旦SCHED_FIFO级的进程获得执行,就会一直执行下去,直到它被阻塞或显示的释放处理器,可见SCHED_FIFO是没有时间片的概念的。只有更高优先级的SCHED_FIFO会SCHED_RR任务才能抢占SCHED_FIFO任务。所以两个级别相同的SCHED_FIFO任务会轮流执行,只有一个让出处理器时另一个才能执行。
SCHED_RR可以看做是带时间片的SCHED_FIFO,SCHED_RR进程的时间片用尽就会轮流调度下一个SCHED_RR进程。
实时优先级范围是0-99,内核用宏MAX_RT_PRIO=100来记录。这个优先级和SCHED_NORMAL级的nice是共享的,如下图:
摒弃实时和非实时,相当于进程优先级的范围是0-139,实时的优先级范围高于普通。
linux的实时调度算法是一种软实时工作方式,即它不保证总能满足这些进程的要求,但他性能已经非常好,且2.6内核后可以满足严格的时间要求。
实时算法的实现都是静态优先级,即进程创建的时候就要确定,且保持不变。相对于普通进程的优先级nice值可以在进程运行过程中进行修改。
从感觉上这种实时的调度策略,可以满足让我们的进程完全占有cpu而不被抢占的可能,至于是不是我所理解的含义,以后继续研究!
本章只是根据<
至于怎么修改普通进程的优先级,和实现实时进程,zai继续学习......。