根据前文,我们知道除了可能会一直占用CPU时间的CPU消耗型进程以外,还有一类叫做IO消耗类型的进程,它们的特点是基本不占用CPU,主要行为是在S状态等待响应。这类进程典型的是vim,bash等跟人交互的进程,以及一些压力不大的,使用了多进程(线程)的或select、poll、epoll的网络代理程序。如果CFS采用默认的策略处理这些程序的话,相比CPU消耗程序来说,这些应用由于绝大多数时间都处在sleep状态,它们的vruntime时间基本是不变的,一旦它们进入了调度队列,将会很快被选择调度执行。对比O1调度算法,这种行为相当于自然的提高了这些IO消耗型进程的优先级,于是就不需要特殊对它们的优先级进行“动态调整”了。
但这样的默认策略也是有问题的,有时CPU消耗型和IO消耗型进程的区分不是那么明显,有些进程可能会等一会,然后调度之后也会长时间占用CPU。这种情况下,如果休眠的时候进程的vruntime保持不变,那么等到休眠被唤醒之后,这个进程的vruntime时间就可能会比别人小很多,从而导致不公平。所以对于这样的进程,CFS也会对其进行时间补偿。补偿方式为,如果进程是从sleep状态被唤醒的,而且GENTLE_FAIR_SLEEPERS属性的值为true,则vruntime被设置为sched_latency_ns的一半和当前进程的vruntime值中比较大的那个。sched_latency_ns的值可以在这个文件中进行设置:
[zorro@zorrozou-pc0 ~]$ cat/proc/sys/kernel/sched_latency_ns
18000000
因为系统中这种调度补偿的存在,IO消耗型的进程总是可以更快的获得响应速度。这是CFS处理与人交互的进程时的策略,即:通过提高响应速度让人的操作感受更好。但是有时候也会因为这样的策略导致整体性能受损。在很多使用了多进程(线程)或select、poll、epoll的网络代理程序,一般是由多个进程组成的进程组进行工作,典型的如apche、nginx和php-fpm这样的处理程序。它们往往都是由一个或者多个进程使用nanosleep()进行周期性的检查是否有新任务,如果有责唤醒一个子进程进行处理,子进程的处理可能会消耗CPU,而父进程则主要是sleep等待唤醒。这个时候,由于系统对sleep进程的补偿策略的存在,新唤醒的进程就可能会打断正在处理的子进程的过程,抢占CPU进行处理。当这种打断很多很频繁的时候,CPU处理的过程就会因为频繁的进程上下文切换而变的很低效,从而使系统整体吞吐量下降。此时我们可以使用开关禁止唤醒抢占的特性。
[root@zorrozou-pc0 zorro]#cat/sys/kernel/debug/sched_features
GENTLE_FAIR_SLEEPERS START_DEBIT NO_NEXT_BUDDY LAST_BUDDY CACHE_HOT_BUDDY WAKEUP_PREEMPTION NO_HRTICK NO_DOUBLE_TICK LB_BIAS NONTASK_CAPACITY TTWU_QUEUE RT_PUSH_IPI NO_FORCE_SD_OVERLAP RT_RUNTIME_SHARE NO_LB_MIN ATTACH_AGE_LOAD
上面显示的这个文件的内容就是系统中用来控制kernel/sched/features.h这个文件所列内容的开关文件,其中WAKEUP_PREEMPTION表示:目前的系统状态是打开sleep唤醒进程的抢占属性的。可以使用如下命令关闭这个属性:
[root@zorrozou-pc0 zorro]#echo NO_WAKEUP_PREEMPTION >/sys/kernel/debug/sched_features
[root@zorrozou-pc0 zorro]#cat/sys/kernel/debug/sched_features
GENTLE_FAIR_SLEEPERS START_DEBIT NO_NEXT_BUDDY LAST_BUDDY CACHE_HOT_BUDDY NO_WAKEUP_PREEMPTION NO_HRTICK NO_DOUBLE_TICK LB_BIAS NONTASK_CAPACITY TTWU_QUEUE RT_PUSH_IPI NO_FORCE_SD_OVERLAP RT_RUNTIME_SHARE NO_LB_MIN ATTACH_AGE_LOAD
其他相关参数的调整也是类似这样的方式。其他我没讲到的属性的含义,大家可以看kernel/sched/features.h文件中的注释。
系统中还提供了一个sched_wakeup_granularity_ns配置文件,这个文件的值决定了唤醒进程是否可以抢占的一个时间粒度条件。默认CFS的调度策略是,如果唤醒的进程vruntime小于当前正在执行的进程,那么就会发生唤醒进程抢占的情况。而sched_wakeup_granularity_ns这个参数是说,只有在当前进程的vruntime时间减唤醒进程的vruntime时间所得的差大于sched_wakeup_granularity_ns时,才回发生抢占。就是说sched_wakeup_granularity_ns的值越大,越不容易发生抢占。
CFS和其他调度策略SCHED_BATCH