Linux性能优化篇-了解CPU上下文切换
我们了解到导致平均负载,有可能是以下几种方面:
- CPU密集型(造成cpu利用率升高,可以理解)
- I/O密集型(io和cpu互斥的,也造成cpu利用率增高-不可中断进程的)
- 大量进程(???)
根据平均负载的解释,单位时间内的处于可运行的进程和不可中断进程的进程数,
System load averages is the average number of processes that are either in a runnable or uninterruptable state. A process in uninterruptable state is waiting for some I/O access, eg waiting for disk
所以我们会比较好了解CPU密集型,需要大量计算资源,会非常消耗cpu,I/O密集型需要等待I/O,会有大量的不可中断进程,
那么大量进程的情况下是如何导致平均负载升高的?
存在大量的进程竞争cpu,也就是存在大量的可运行进程,但是我们都知道cpu一个时间段其实只能运行一个进程,其他的进程也都只是在等待,并不会大量耗尽cpu资源的情况,而是cpu按照时间片给大量进程运行,所以可以想象在不停的切换不停的进程的情况下是不是也在消耗cpu资源能力,也就是我们今天要说的cpu上下文切换。
Linux是一个多用户用任务的操作系统,他支持远远大于cpu的进程数运行,而cpu每次却只能运行一个任务,所以其实这些任务其实并不是在同时运行,整个过程是cpu轮流运行任务,给用户带来的假象。而当一个任务需要运行前,cpu需要知道从那里加载,从哪里开始运行,也就是系统需要事先设置好cpu寄存器和程序计数器。
- cpu寄存器: cpu内置的容量小,但是速度极快的内存。
- 程序计数器: 用来存储cpu正在执行的指令位置,或者即将执行的下一条指令位置。
它们是CPU在运行任何任务前,必须依赖的,所以也被称为CPU上下文。
所以根据上下文的了解,所谓的上下文切换:
- 把前一个任务的CPU上下文保存下来
- 找到并加载新任务的上下文
- 切换到新的cpu寄存器和程序计数器最后跳到程序计数器所指的新位置,开始运行新任务。
保存下来的上下文会存储在内核,并在任务重新调度时再加载进去,这样任务不受影响,任务看起来来也是连续运行的。
根据任务的不同,CPU上下文切换可以分几种不同场景:
- 进程上下文切换
- 线程上下文切换
- 中断上下文切换
进程上下文切换
Linux分为内核空间和用户空间:
所以根据内核空间划分,一个进程可以被称为进程的用户态和进程的内核态,
当一个系统调用发生市必然会发生CPU上下文切换的,
比如我们加载存在磁盘里的一个文件,我们触发的指令是进程的用户态,而我们需要操作磁盘就需要系统调用陷入内核态,而我们原先的用户态就需要保存起来,执行内核态的指令,加载完数据就需要恢复用户态,继续运行进程,
所以可以知道一次系统调用会发生两次CPU上下文切换。
这里说的系统调用和进程上下文切换又是不同的:
- 进程上下文切换是指从一个进程切换到另一个进程
- 系统调用始终在一个进程中运行
所以系统调用还是被称为特权模式调用,而不是上下文切换,但是系统调用导致的cpu上下文切换还是不可避免。
那么进程的上下文切换和系统调用存在什么关系?
进程是需要系统内核来管理和调度的,进程的上下文切换只能发生在内核态,
所以进程的上下文不仅包括虚拟内存、栈、全局变量等用户空间资源,还有内核堆栈、寄存器等内核空间状态。
所以进程的上下文切换和系统调用(软中断)多了一个保存用户空间和恢复用户空间。
进程上下文切换过程:
- 接受切换信号,挂起进程,记录当前的进程的虚拟内存、栈等资源存储
- 将这个进程在CPU的上下文状态存储
- 然后检索下一个进程的CPU的上下文
- 加载到CPU的寄存器中恢复
- 还需要刷新进程保存的虚拟内存和用户栈
- 最后跳转到程序计数器所指向的位置,恢复进程运行
而保存上下文和恢复上下文过程不是免费的,大概每次上下文切换会花费几十纳秒到数微妙之间,当大量进程时,这个cpu上下文切换是相当可观的,会花费大量时间在保存和恢复cpu上下文和用户空间状态,cpu分配给进程的时间片是一定的,导致cpu实际运行进程时间大大减少,而当时间片用完,进程必须挂起,这也是导致平均负载升高的一个因素。
什么时候需要进行进程上下文切换?
Linux会为每个cpu都维护一个就绪队列,也就是进程状态为R状态的的进程,最理想状态是之前的进程完成,cpu得到释放,下一个进程得到cpu使用,但是实际情况是不同的。
- 为了保证所有进程得到公平调度,CPU时间被划分一段段时间片,这些时间片轮流分给进程,当时间片耗尽,进程会被挂起,等待下一次分配cpu时间片。
- 进程运行的系统资源不足,比如内存不足,进程必须得倒资源满足才可以运行,这个时候会被挂起,系统会调度其他可运行的进程。
- 当进程通过睡眠函数主动挂起,会重新调度。
- 当有高优先级的进程运行时,为保障高优先级运行,当前进程会被挂起,由高优先级进程运行。
- 发生硬件中断,cpu上的进程会被挂起,而由内核中的中断服务运行。
线程上下文切换
线程和进程的最大区别在于,线程是调度的基本单位,而进程则是资源拥有的基本单位,说白了,内核中的任务调度,调度的对象就是线程,而进程给线程提供虚拟内存、全局变量等资源。
进程与线程的比较:
- 当进程只有一个线程的时候,进程就等于线程
- 当进程由多个线程的时候,这些线程会共享相同的虚拟内存与全局变量等资源,这些在上下文切换的时候不需要修改
- 线程也是有私有数据的,这些数据在上下文切换的时候是需要保存的
所以这里也可以看出,相对于比较多进程与多线程,
多线程间的切换会比多进程间的切换消耗更少的资源。
中断上下文切换
- 中断
- 软中断可以触发内核执行 We know that we can trigger the kernel to execute by generating a software interrupt.
-
int
指令可以产生软中断
Kernel-side: int $0x80 entry point
为了快速响应硬件事件,中断处理会打断进程的正常调度和执行,转而执行调用中断处理程序,响应设备事件。但是和进程的上下文不同的是,
中断上下文切换不会涉及到进程的用户态,只是需要内核态中断程序执行必需的状态,并且对于一个cpu来说,中断处理比进程拥有更高的优先级。
怎么查看系统上下文切换情况
过多的cpu上下文切换会导致花费大量的时间消耗在寄存器、内核栈及虚拟内存的保存与恢复中,缩短cpu在规定时间片内真正运行的时间,导致系统性能大幅下降。
vmstat可以用来分析系统内存使用情况,也可以用来分析cpu上下文切换和中断的次数。
- r: Running or Runnable Task 是就绪队列的长度,也就是正在运行和等待CPU的进程数
- b: Blocked Task 处于不可中断睡眠状态的进程数
- cs: Context switch 是每秒上下文切换的次数
- in: Interrupt 则是每秒中断的次数
vmstat可以给出总体的上下文切换情况,如果想要查看每个进程的详细情况,
需要pidstat来查看:
- cswch: 表示每秒自愿上下文切换的次数
- nvcswch: 表示每秒非自愿上下文切换
这两种概念意味着不同的性能问题:
- 自愿上下文切换,是指进程无法获取所需资源,导致的上下文切换,如I/O,内存不足等问题,就会发生大量的自愿上下文切换。
- 非自愿上下文切换,是指进程由于时间片已到,被系统强制调度,而发生的上下文切换,比如大量进程争抢cpu,会发生大量非自愿上下文切换。
上下文切换频率多少才算合适
使用sysbench模拟多线程调度瓶颈:
查看vmstat:
cs(上下文切换)突然增大到300多万,同时观测到r增大到9,远远超过了操作系统的2核,us(user)和sy(system)这两列cpu的使用率开始突增,cpu主要被内核空间占有,in列也开始增加一倍,说明中断也是一个原因。
我们从vmstat大体可以看出cpu上下文切换和中断,并且存在多个进程竞争情况,内核空间异常繁忙,接下来我们需要分析原因,需要继续分析:
很明显看出来cpu使用率升高是sysbench导致的,而上下文切换则是其他进程,包括非自愿上下文切换最高的pidstat,但是我们会发现自愿上下文切换比vmstat来说的300多万来说小太多了,我们需要考虑线程问题。
还有我们要如何查看中断突然增大:
watch -d cat /proc/interrupts
Rescheduling interrupts are the Linux kernel's way to wake-up an idle CPU-core to schedule a thread on it. On SMP systems, this is often done by the scheduler in a effort to spread the load across multiple CPU-cores. 重新安排中断是Linux内核唤醒空闲CPU核心以在其上安排线程的方法.在SMP系统上,这通常由调度程序完成,以便将负载分散到多个CPU核心 Function call interrupts:: software-interrupts to 软中断
所以回到上下文切换多少合适,这个数值还是取决于cpu性能,如果想要系统比较稳定,这个值可以尽量控制在几百到一万之间,如果超过一万或者指数级增量,一般都是出现性能问题。
总结:
sysbench是一款开源的多线程性能测试工具,可以执行CPU/内存/线程/IO/数据库等方面的性能测试
- 自愿上下文切换变多,说明进程在等待资源,可能I/O等其他问题
- 非自愿上下文切换变多,说明进程被强制调度,争抢cpu,cpu是瓶颈
- 中断次数增多,说明cpu被中断处理程序占用,要通过/proc/interrupts文件来分析具体的中断类
来源:极客