Linux性能优化篇-了解CPU上下文切换

我们了解到导致平均负载,有可能是以下几种方面:

  1. CPU密集型(造成cpu利用率升高,可以理解)
  2. I/O密集型(io和cpu互斥的,也造成cpu利用率增高-不可中断进程的
  3. 大量进程(???)

根据平均负载的解释,单位时间内的处于可运行的进程和不可中断进程的进程数

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寄存器和程序计数器。

  1. cpu寄存器: cpu内置的容量小,但是速度极快的内存。
  2. 程序计数器: 用来存储cpu正在执行的指令位置,或者即将执行的下一条指令位置。

它们是CPU在运行任何任务前,必须依赖的,所以也被称为CPU上下文。

所以根据上下文的了解,所谓的上下文切换:

  1. 把前一个任务的CPU上下文保存下来
  2. 找到并加载新任务的上下文
  3. 切换到新的cpu寄存器和程序计数器最后跳到程序计数器所指的新位置,开始运行新任务。

保存下来的上下文会存储在内核,并在任务重新调度时再加载进去,这样任务不受影响,任务看起来来也是连续运行的。

根据任务的不同,CPU上下文切换可以分几种不同场景:

  • 进程上下文切换
  • 线程上下文切换
  • 中断上下文切换

进程上下文切换

Linux分为内核空间和用户空间:

所以根据内核空间划分,一个进程可以被称为进程的用户态和进程的内核态,

当一个系统调用发生市必然会发生CPU上下文切换的,

比如我们加载存在磁盘里的一个文件,我们触发的指令是进程的用户态,而我们需要操作磁盘就需要系统调用陷入内核态,而我们原先的用户态就需要保存起来,执行内核态的指令,加载完数据就需要恢复用户态,继续运行进程,

所以可以知道一次系统调用会发生两次CPU上下文切换。

这里说的系统调用和进程上下文切换又是不同的:

  1. 进程上下文切换是指从一个进程切换到另一个进程
  2. 系统调用始终在一个进程中运行

所以系统调用还是被称为特权模式调用,而不是上下文切换,但是系统调用导致的cpu上下文切换还是不可避免。

那么进程的上下文切换和系统调用存在什么关系?

进程是需要系统内核来管理和调度的,进程的上下文切换只能发生在内核态,

所以进程的上下文不仅包括虚拟内存、栈、全局变量等用户空间资源,还有内核堆栈、寄存器等内核空间状态

所以进程的上下文切换和系统调用(软中断)多了一个保存用户空间和恢复用户空间。

进程上下文切换过程:

  1. 接受切换信号,挂起进程,记录当前的进程的虚拟内存、栈等资源存储
  2. 将这个进程在CPU的上下文状态存储
  3. 然后检索下一个进程的CPU的上下文
  4. 加载到CPU的寄存器中恢复
  5. 还需要刷新进程保存的虚拟内存和用户栈
  6. 最后跳转到程序计数器所指向的位置,恢复进程运行

而保存上下文和恢复上下文过程不是免费的,大概每次上下文切换会花费几十纳秒到数微妙之间,当大量进程时,这个cpu上下文切换是相当可观的,会花费大量时间在保存和恢复cpu上下文和用户空间状态,cpu分配给进程的时间片是一定的,导致cpu实际运行进程时间大大减少,而当时间片用完,进程必须挂起,这也是导致平均负载升高的一个因素。

什么时候需要进行进程上下文切换?

Linux会为每个cpu都维护一个就绪队列,也就是进程状态为R状态的的进程,最理想状态是之前的进程完成,cpu得到释放,下一个进程得到cpu使用,但是实际情况是不同的。

  1. 为了保证所有进程得到公平调度,CPU时间被划分一段段时间片,这些时间片轮流分给进程,当时间片耗尽,进程会被挂起,等待下一次分配cpu时间片。
  2. 进程运行的系统资源不足,比如内存不足,进程必须得倒资源满足才可以运行,这个时候会被挂起,系统会调度其他可运行的进程。
  3. 当进程通过睡眠函数主动挂起,会重新调度。
  4. 当有高优先级的进程运行时,为保障高优先级运行,当前进程会被挂起,由高优先级进程运行。
  5. 发生硬件中断,cpu上的进程会被挂起,而由内核中的中断服务运行。

线程上下文切换

线程和进程的最大区别在于,线程是调度的基本单位,而进程则是资源拥有的基本单位,说白了,内核中的任务调度,调度的对象就是线程,而进程给线程提供虚拟内存、全局变量等资源。

进程与线程的比较:

  1. 当进程只有一个线程的时候,进程就等于线程
  2. 当进程由多个线程的时候,这些线程会共享相同的虚拟内存与全局变量等资源,这些在上下文切换的时候不需要修改
  3. 线程也是有私有数据的,这些数据在上下文切换的时候是需要保存的

所以这里也可以看出,相对于比较多进程与多线程,

多线程间的切换会比多进程间的切换消耗更少的资源。

中断上下文切换

  • 中断
  1. 软中断可以触发内核执行 We know that we can trigger the kernel to execute by generating a software interrupt.
  2. int 指令可以产生软中断

Kernel-side: int $0x80 entry point

为了快速响应硬件事件,中断处理会打断进程的正常调度和执行,转而执行调用中断处理程序,响应设备事件。但是和进程的上下文不同的是,

中断上下文切换不会涉及到进程的用户态,只是需要内核态中断程序执行必需的状态,并且对于一个cpu来说,中断处理比进程拥有更高的优先级。

怎么查看系统上下文切换情况

过多的cpu上下文切换会导致花费大量的时间消耗在寄存器、内核栈及虚拟内存的保存与恢复中,缩短cpu在规定时间片内真正运行的时间,导致系统性能大幅下降。

vmstat可以用来分析系统内存使用情况,也可以用来分析cpu上下文切换和中断的次数。

  1. r: Running or Runnable Task 是就绪队列的长度,也就是正在运行和等待CPU的进程数
  2. b: Blocked Task 处于不可中断睡眠状态的进程数
  3. cs: Context switch 是每秒上下文切换的次数
  4. in: Interrupt 则是每秒中断的次数

vmstat可以给出总体的上下文切换情况,如果想要查看每个进程的详细情况,

需要pidstat来查看:

  1. cswch: 表示每秒自愿上下文切换的次数
  2. 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文件来分析具体的中断类

来源:极客

原文发布于微信公众号 - 架构说(JiaGouS)

原文发表时间:2019-08-20

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券