讲述如何理解平均负载的时候,平均负载的主要来源是多个进程竞争CPU
但是进程等待CPU的时候,为何会导致系统的负载升高?来源于CPU上下文切换
Linux是一个多任务操作系统,支持远大于CPU数量的任务同事运行,当然这些任务并不是真正的同时运行,因为系统在很短的时间内,进行的任务切换执行,造成了多任务同时运行的错觉
那么每次切换任务的时候,CPU都需要知道任务从哪里加载,从任务哪里开始运行,这里用了CPU寄存器和程序计数器 Program Counter
CPU寄存器,是CPU内置的容量小,但时速度极快的内存,程序计数器,存储CPU执行的指令所在位置,这两者合起来,称为CPU上下文
说完了CPU上下文的概念,就可以理解CPU上下文切换的概念,就是将前一个任务的CPU上下文保存起来,然后加载新的任务的上下文的寄存器和程序计数器,最后跳转到程序计数器所在的新位置,运行新任务
保存下来的上下文,会存储在系统内核中,在任务重新调度执行的时候再次加载,保证原本任务状态不受影响,任务还是看起来连续的运行
那么CPU上下文切换就是更新了CPU寄存器的值,将自己的状态保存了起来
那么对应的能够触发线程上下文切换的,就是线程切换和中断任务
那么这几种任务的上下文切换,如何和CPU性能挂钩的
1.首先是线程组/主线程/进程的上下文切换
因为Linux中包含用户态和内核态
所以进程一般在用户态运行,需要系统调用的时候,就需要将自己转变为内核态
常见的系统调用,比如文件系统的查看,就是利用了open系统调用,read write到标准输出
这几个常见的系统调用
然后系统调用的时候必然涉及了CPU上下文的切换
CPU寄存器中原本是用户态的指令位置,先保存成对应的数据结构,放到寄存器中,然后执行内核态代码,CPU寄存器需要更新为内核态指令的新位置,最后跳转到内核态执行内核任务
CPU寄存器还需要恢复为用户态,切换到用户空间
故一次系统调用,需要两次CPU上下文切换
进程之间的上下文切换,比起系统调用,多了异步,就保存当前进程的内核状态和CPU寄存器之前,先将进程的虚拟内核,栈保存下来,加载下一个进程的内核态之后,还需要刷新进程的虚拟内存和用户栈
而这个内核状态和CPU寄存器的保存,就需要CPU上运行才行
而这个保存,就是大量进程上下文切换的时候,消耗的时间
那么进程什么时候才会切换上下文呢?
进程切换需要切换上下文,只有需要进程调度的时候,才需要切换上下文Linux为每个CPU都维护了一个就绪队列,将活跃进程按照优先级和等待CPU的时间排序
理想状态下,就是进程执行完成了,将之前的CPU获取出来.拿一个新的进程过来使用
但是实际上,
为了保证所有的进程可以公平调用,CPU将自己分为一段段的时间片,轮流分配给各个进程,当某个进程的时间片耗尽了,就轮到其他等待CPU的进程运行了
或者进程需要的资源不足的时候,需要挂起等待
或者进程自己利用sleep进行主动挂起
优先级更高的进程运行的时候,会将当前低优先级的进程挂起
最后就是中断任务导致的挂起
2.接下来是线程的切换
进程和线程的区别在于,线程是基本的调度单位,进程则是一个资源拥有的基本单位
调度进程的时候,实际被调度的是线程
当进程只有一个线程的时候,进程就等于线程
当进程有多个线程的时候,线程共享相同的内核和全局变量,如果是进程内线程切换,不需要修改这些资源
但是线程有自己的私有数据,上下文切换还是需要保存
3.最后是中断上下文切换
中断会打断进程的正常调度和执行,为了快速的响应硬件的事件,在打断其他进程的时候,需要讲进程当前的状态保存下来,中断结束后,进程仍然可以从原来的状态恢复
进程上下文不同的是,中断上下文不涉及进程的用户态,中断过程打断了一个正在处于用户态的进程,也不需要保存和恢复这个进程的虚拟内存,全局变量等用户态资源,中单上下文,只需要保存内核态中断服务程序执行必需的状态
对于CPU来说,中断事件优先级极高,所以中断会打断正常进程调度和执行,所以中断处理程序都短小精悍
但是这也是造成系统性能下降的重要原因,中断次数多的时候,需要注意和排查是否会给系统带来严重的性能问题
总结一下
CPU上下文切换,是Linux系统正常工作的核心功能之一,一般不需要特别关注
但是过多的上下文切换,会将CPU消耗在寄存器 内核栈 这些数据的保存和恢复上
导致系统性能被占用