目录
[性能优化] - cpu调度原理
second60 20180801
1 背景
linux系统是一个多任务实时操作系统。这里的多任务就CPU调度的任务。
在linux操作系统中,CPU是并不知道进程存在的,进程是以任务task_structure的形式让CPU分时间片一个一个去处理。
问题来了:CPU是怎么做到多任务的切换分时间片的呢?是的,答案就是CPU调度机制。在多任务处理机制下,多个程序共享CPU,它们在CPU上轮流运行。
2 基本概念
任务:CPU执行的一个时间片。内核中会对每个进程task_structure,优先级和阻塞率等,进 行分时间片。
调度机制:对任务进行切换调度的一种机制。
一个单独的CPU,同一时间只能对一个任务进行处理。
3 CPU调度程度
内核使用进程调度程序来确定哪个程序在哪个给定时间点运行。为了工作正常,进程调度程序必须合理调度不同资源。它必须很快确定接下来轮到哪个进程得到CPU。保证各进程得到的CPU时间是公平的,同时允许高优先级的进程得到更大的CPU时间,或抢占较低优先级的进程CPU时间。
三种调度程序
- O(n)调度程序
- O(1)调度程序
- CFS调度程序
3.1 O(n)调度程序
这是内核2.6以前的调度程序,原理是:它必须扫描整个进程列表,根据优先级等条件,以便找到下一个要运行的进程。采用的是轮询的方法。
O(n)调度程序的时间复杂度为O(n),如果进程越多,轮询时间越长,效率也越差。
3.2 O(1)调度程序
内核2.6中提出并使用,原理:为每个CPU使用2个队列,一个运行队列和一个进期队列。调度程序根据它们的优先级将它们放置在运行队列的进程列表中,需要调度时,取出运行队列中的最高优先级列表中的第一个进程,并运行它。
调度程序基于进程优先级和以前的阻塞率给进程分配一个时间片,当进程时间片用完后,调度程序将其移动到过期队列相应的优先级列表。然后它从运行列中取出下一个具有最高优先级的进程,重复以上过程。一旦运行队列中不再有进程等待,调度程序就将过期队列转变为新的运行队列,之前的运行队列成为新的过期队列,开始再次循环。
流程:
- 调度程序根据优先级将进程放置在运行队列
- 调度程序将从运行队列取出最高优先级的第一个进程运行
- 调度程序基于进程优先级和以前的阻塞率给进程分配一个时间片
- 调度程序将其移动到过期队列相应的优先级列表
- 从运行列中取出下一个具有最高优先级的进程,重复以上过程
- 当运行队列为空时,过期队列和运行队列变换,开次开始循环。
优点:
- 时间复杂度为O(1),效率好,每个进程都会执行,不会饿死低优先级进程。
- 支持非统一内存架构NUMA
- 支持对称多线程处理器SMP
缺点:交互式进程会得到较高的优先级,拥有较长的时间片。
问题:
- 多CPU时,怎么来负载均衡进程数量?(进程数平均)
- 怎么来保证每个CPU的能比较平均地运行?(时间平均)
- 会不会出现某个CPU运行效率很高,某个CPU运行效率很低?
3.3完全公平调度程序CFS
CFS(Completely Fair Scheduler) 完全公平调度程序,在内核 2.6.23中引入,因为不能解决上面的问题,所以用来取化O(1)。
CFS基于“虚拟时间”的红黑树。虚拟时间基于进程等待运行时间、竞争CPU进程数量及进程优先级来计算。
具有最多虚拟时间的进程(最长等待CPU时间)得到使用CPU的权限。随着它使用CPU时间的增加,它的虚拟时间在减少。一旦进程不再拥有最多的虚拟时间,它将被拥有最多虚拟时间的进程抢占。
流程:
- 调度程序根据进程等待运行时间、竞争CPU进程数量及进程优先级来计算进程虚拟时间
- 调度程序取出虚拟时间最多的进程并运行
- 虚拟时间随着使用时间而减少
- 进程不再拥有最多的虚拟时间,它将被拥有最多虚拟时间的进程抢占。
优点:
- 代码简单
- 性能比较好
- 多核中,每个进程也比较均衡地分到时间片
4 CPU与性能优化
CPU是性能优化,最直接的一方面。通过观察CPU使用情况,可以了解当前系统CPU的负载,进程的使用情况是否有异常等。
CPU衡量标准:
- CPU使用率:描述了每个处理器整体使用率。
- 如果在一段时间持续时间内CPU使用率超过80%,则要查看是什么原因导致CPU持续高:高峰期,代码逻辑问题,定时批量处理等
- 如果是正常逻辑,那么可能是处理器瓶劲,是否应考虑分布式处理,或换更好CPU的机器。
- CPU高,通常情况下是一件好事,说明CPU使用率高。如果持续高,那么也要考虑下高峰期高并发的临时方案,如启运个备用进程防止负载过高;拆分业务到不同进程里,分多台机器处理;给进程一个最大限制的处理业务量,当超过这个量就不再处理新业务或等待处理等。
- 用户进程消耗CPU的时间:描述了CPU花费在用户进程的百分比。
- 如果值比较高,说明进程处理利用率比较好。如果多核的值比较平均是更好了。
- 但一直过高,可以通过火陷图等工具,查看进程函数的调用是否正常,如果不正常那么可对代码进行优化,如算法的优化,循环的优化,调用次数的优化等。
- 内核操作消耗CPU时间:描述了CPU花费在内核操作的百分比,包括IRQ(中断请求)和softirq时间。
- 内核消耗的CPU,主要是在中断请求,网卡,驱动程序等。如果CPU越高,那么说明可能存在瓶颈,如网卡并发十万数据,sys非常高,说明sys大部份时间花在了网卡中断上。
- 正常情况下,内核操作上的时间尽可能少。
- 等待:CPU花费在等待上的时间总量。
- 如IO操作等待,阻塞
- 系统不应花太多时间在等待上
- 如果等待时间太长,应检查IO子系统性能,是否IO操作不过来,或磁盘损坏。
- 如果IO操作不过来,是否考虚缓存设计,或更新更快速度磁盘等
- CPU空闲时间:描述了系统空闲等待任务的CPU百分比。
- 为了最大效率的使用CPU,当然是CPU空闲时间越少越好
- nice消耗CPU时间:描述了CPU花费在re-nicing进程()更改进程执行顺序和优先级) 的时间百分比。
- 这个也不用说了,越小越好,小说明执行变更少。
- 平均负载:是1min,5min,15min, 队列中等待处理进程数+等待不可中断任务被完成的进程数之和。
- load average分1min, 5min, 15min。
- 队列中等待处理的进程数(TASK_RUNNING状态的进程)
- 等待不可中断任务被完成的进程数(TASK_UNINTERRUPTIBLE状态的进程)
- 如果进程请求CPU时间被阻塞,负载增加
- 如果每个进程得到直接访问CPU的时间,没有在CPU周期丢失,负载减小
- 很多文章说,负载的值为核数的两倍或三倍时,是比较优的。比如,四核,那么负载为8是比较优的,因为一个正在运行,一个正在等待运行。建议两倍。
- 可运行的进程:描述已经准备好执行的进程数。
- 在一段持续时间内,这个值不应该超过处理器数量的10倍。
- 超进10倍,可能是处理器瓶颈
- 阻塞的进程:不被执行的进程数。
- 因为进程要等待IO操作结整,所以阻塞
- 阻塞的进程数能反映出是否有IO瓶颈
- 上下文切换:系统发生线程之间切换的数量。
- 上下文切换越少越好,切换意味着CPU的无用消耗,导致CPU缓存刷新。
- 大量上下文切换如果与大量中断有关,可能是驱动程序或应用程出现问题。
- 中断:包括硬中断、软中断、CPU时钟中断等。
- 硬中断:硬件发出的消息导致中断,如键盘按键、鼠标点击、网卡网络数据到达等。
- 软中断:代码引发的中断,如除0操作、空指针操作等。
- 较高的中断值,可能是软件的瓶劲,也可能是驱动程序的瓶颈。
5 总结
本节讲解了CPU的调度原理,能让读者了解CPU是怎么样对任务(进程)进行调度的。同时结合实际,分享了CPU的实际中的性能分析。