[转帖]Linux性能分析(二):理解CPU上下文切换

linux,性能,分析,理解,cpu,上下文,切换 · 浏览次数 : 0

小编点评

每秒上下文切换多少次才算正常呢?这个要看具体情况:如果系统的上下文切换次数比较稳定,那么从数百到一万以内,都应该算是正常的。但当上下文切换次数超过一万次,就可能已经出现了性能问题,这个时候我们可以借助 vmstat 、 pidstat 和 /proc/interrupts 等工具,来辅助排查性能问题的根源。 具体而言,每秒上下文切换的次数取决于系统的性能和调度器的配置。如果系统的性能稳定,调度器可以根据需要进行调整,降低上下文切换的次数。而当系统性能不稳定时,调度器通常会提高上下文切换的次数,以提高系统的性能。

正文

在计算机中,上下文切换是指存储进程或线程的状态,以便以后可以还原它并从同一点恢复执行。这允许多个进程共享一个CPU,这是多任务操作系统的基本功能。
Linux 是一个多任务操作系统,它支持远大于 CPU 数量的任务同时运行,这依赖于CPU上下文切换。CPU 上下文切换,就是先把前一个任务的 CPU 上下文(也就是 CPU 寄存器和程序计数器)保存起来,然后加载新任务的上下文到这些寄存器和程序计数器,最后再跳转到程序计数器所指的新位置,运行新任务;而这些保存下来的上下文,会存储在系统内核中,并在任务重新调度执行时再次加载进来。这样就能保证任务原来的状态不受影响,让任务看起来还是连续运行。

Linux上下文切换有三种潜在的触发条件:多任务上下文切换、中断处理上下文切换以及用户和内核模式切换。

多任务

最常见的是,在某些调度方案中,必须将一个进程从CPU中切换出来,以便另一个进程可以运行。可以通过使自身无法运行的过程来触发此上下文切换,例如,等待I/O或同步操作完成。在抢先式多任务系统上,调度程序还可以切换出仍可运行的进程。Linux 为每个 CPU 都维护了一个就绪队列,将活跃进程(即正在运行和正在等待 CPU 的进程)按照优先级和等待 CPU 的时间排序,然后选择最需要 CPU 的进程,也就是优先级最高和等待 CPU 时间最长的进程来运行。

多任务中,除了进程上下文切换,还包括线程上下文切换,线程与进程最大的区别在于,线程是调度的基本单位,而进程则是资源拥有的基本单位。说白了,所谓内核中的任务调度,实际上的调度对象是线程;而进程只是给线程提供了虚拟内存、全局变量等资源;另外,线程也有自己的私有数据,比如栈和寄存器等,这些在上下文切换时也是需要保存的。如果前后两个线程属于不同进程,此时,因为资源不共享,所以切换过程就跟进程上下文切换是一样。如果前后两个线程属于同一个进程。此时,因为虚拟内存是共享的,所以在切换时,虚拟内存这些资源就保持不动,只需要切换线程的私有数据、寄存器等不共享的数据。

中断处理

现代架构是中断驱动的。这意味着,例如,如果CPU从磁盘请求数据,则不需要忙于等待读取结束。它可以发出请求并继续执行其他操作。读取结束后,可以中断 CPU 并显示读取内容。对于中断,将安装一个称为中断处理程序的程序,该中断处理程序将处理来自磁盘的中断。

为了快速响应硬件的事件,中断处理会打断进程的正常调度和执行,转而调用中断处理程序,响应设备事件。而在打断其他进程时,就需要将进程当前的状态保存下来,这样在中断结束后,进程仍然可以从原来的状态恢复运行。

对同一个 CPU 来说,中断处理比进程拥有更高的优先级,所以中断上下文切换并不会与进程上下文切换同时发生。同样道理,由于中断会打断正常进程的调度和执行,所以大部分中断处理程序都短小精悍,以便尽可能快的执行结束。

另外,跟进程上下文切换一样,中断上下文切换也需要消耗 CPU,切换次数过多也会耗费大量的 CPU,甚至严重降低系统的整体性能。所以,当你发现中断次数过多时,就需要注意去排查它是否会给你的系统带来严重的性能问题。

用户和内核模式切换

Linux 按照特权等级,把进程的运行空间分为内核空间(Ring 0)和(Ring 3),进程在用户空间运行时,被称为进程的用户态,而陷入内核空间的时候,被称为进程的内核态:

  • 内核空间(Ring 0)具有最高权限,可以直接访问所有资源;
  • 用户空间(Ring 3)只能访问受限资源,不能直接访问内存等硬件设备,必须通过系统调用陷入到内核中,才能访问这些特权资源。

当操作系统需要在用户模式和内核模式之间转换时,不需要上下文切换;模式转换本身并不是上下文切换。但是,在Linux系统中,从用户态到内核态的转变,需要通过系统调用来完成。CPU 寄存器里原来用户态的指令位置,需要先保存起来,接着,为了执行内核态代码,CPU 寄存器需要更新为内核态指令的新位置,最后才是跳转到内核态运行内核任务。而系统调用结束后,CPU 寄存器需要恢复原来保存的用户态,然后再切换到用户空间,继续运行进程。所以,一次系统调用的过程,其实是发生了两次 CPU 上下文切换。不过,需要注意的是,系统调用过程中,并不会涉及到虚拟内存等进程用户态的资源,也不会切换进程。

查看系统上下文切换

我们通常使用vmstatpidstat来查看系统上下文切换,先看vmstat

#每隔2秒输出一组数据,一共输出5组
$ vmstat 2 5
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 1  0    524 2455820 206824 3795240    0    0    14   148   78  153  1  1 98  0  0
 0  0    524 2455820 206828 3795272    0    0     0    22  262  488  0  0 99  0  0
 1  0    524 2455820 206828 3795276    0    0     0     0  252  472  0  0 100  0  0
 0  0    524 2455820 206828 3795276    0    0     0     0  249  482  0  0 100  0  0
 0  0    524 2455820 206828 3795276    0    0     0     0  245  469  0  1 99  0  0

    重点关注如下几组数据:

    cs(context switch)每秒上下文切换次数。
    in(interrupt)每秒中断次数。
    r(Running or Runnable)正在运行和等待 CPU 的进程数量。
    b(Blocked)处于不可中断状态的进程数量。
    

      再看pidstat

      $ pidstat -w 3
      Linux 5.0.0-32-generic (ubuntu)         10/26/2019      _x86_64_        (2 CPU)
      

      09:44:58 AM UID PID cswch/s nvcswch/s Command
      09:45:01 AM 0 9 0.33 0.00 ksoftirqd/0
      09:45:01 AM 0 10 7.28 0.00 rcu_sched

        重点关注如下两组数据:

        cswch:每秒自愿上下文切换(voluntary context switches)的次数。自愿上下文切换,是指进程无法获取所需资源,导致的上下文切换。比如说, I/O、内存等系统资源不足时,就会发生自愿上下文切换。
        nvcswch:表示每秒非自愿上下文切换(non voluntary context switches)的次数。非自愿上下文切换,则是指进程由于时间片已到等原因,被系统强制调度,进而发生的上下文切换。比如说,大量进程都在争抢 CPU 时,就容易发生非自愿上下文切换。
        

          实战案例

          预先安装 sysbenchsysstat 包:

          $ sudo apt install sysstat sysbench
          

            然后先打开一个终端运行sysbench

            # 以 5 个线程运行 10 分钟的基准测试,模拟多线程切换的问题
            $ sysbench --threads=5 --max-time=600 threads run
            

              在第二个终端打开vmstat查看上下文切换:

              $ vmstat 2 5
              procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
               r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
               5  0    524 2447664 207216 3796324    0    0    14   144   81  317  1  1 98  0  0
               5  0    524 2447656 207216 3796324    0    0     0     0 1485 1983261 21 79  0  0  0
               5  0    524 2447656 207216 3796324    0    0     0     0 3421 1883889 18 82  0  0  0
               5  0    524 2446032 207220 3796368    0    0     0    32 1973 1909547 20 79  1  0  0
               5  0    524 2446032 207220 3796368    0    0     0     0 2232 1982718 23 78  0  0  0
              

                你应该可以发现,cs 列的上下文切换次数骤然上升到了 190 万,就绪队列r的长度已经到了 5,ussyCPU 使用率加起来上升到了 100%,中断in列也有2000多,看起来也是个问题。

                那么到底是什么进程导致了这些问题呢?我们在第三个和第四个终端分别打开toppidstat

                $ top
                top - 10:12:14 up 17:35,  5 users,  load average: 5.19, 3.35, 1.48
                Tasks: 331 total,   1 running, 261 sleeping,   0 stopped,   0 zombie
                %Cpu(s): 21.6 us, 78.3 sy,  0.0 ni,  0.2 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
                KiB Mem :  8140984 total,  2446660 free,  1690512 used,  4003812 buff/cache
                KiB Swap:  2097148 total,  2096624 free,      524 used.  6125044 avail Mem 
                

                PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
                24329 simplei+ 20 0 119440 8120 6544 S 198.0 0.1 8:11.31 sysbench
                24354 simplei+ 20 0 51452 4224 3396 R 0.7 0.1 0:00.10 top
                10 root 20 0 0 0 0 I 0.3 0.0 0:24.95 rcu_sched

                  # 每隔 1 秒输出 1 组数据(需要 Ctrl+C 才结束)
                  # -wt 参数表示输出线程的上下文切换指标,而 -u 参数则表示输出 CPU 使用指标
                  $ pidstat -wt -u 1
                  Average:      UID      TGID       TID    %usr %system  %guest   %wait    %CPU   CPU  Command
                  Average:     1000     24329         -   39.91  100.00    0.00    0.00  100.00     -  sysbench
                  Average:     1000         -     24330    8.45   29.11    0.00   57.75   37.56     -  |__sysbench
                  Average:     1000         -     24331    7.51   28.17    0.00   57.75   35.68     -  |__sysbench
                  Average:     1000         -     24332    6.10   29.58    0.00   58.22   35.68     -  |__sysbench
                  Average:     1000         -     24333    8.45   28.64    0.00   58.22   37.09     -  |__sysbench
                  Average:     1000         -     24334    8.92   28.64    0.00   56.34   37.56     -  |__sysbench
                  

                  Average: UID TGID TID cswch/s nvcswch/s Command
                  Average: 1000 - 24330 575.12 360744.60 |__sysbench
                  Average: 1000 - 24331 2152.11 334425.35 |__sysbench
                  Average: 1000 - 24332 1810.80 348483.10 |__sysbench
                  Average: 1000 - 24333 214.08 366587.79 |__sysbench
                  Average: 1000 - 24334 872.77 358510.80 |__sysbench

                    toppidstat都可以看出CPU 使用率的升高果然是 sysbench 导致的,它的 CPU 使用率已经达到了 100%。另外可以看出sysbench的子线程的上下文切换次数非常多。
                    最后我们还要在新的中断查看下中断次数:

                    # -d 参数表示高亮显示变化的区域
                    $ watch -d cat /proc/interrupts
                                CPU0       CPU1
                     RES:     982564    1036295   Rescheduling interrupts
                    

                      观察一段时间,你可以发现,变化速度最快的是重调度中断(RES),这个中断类型表示,唤醒空闲状态的 CPU 来调度新的任务运行。这是多处理器系统(SMP)中,调度器用来分散任务到不同 CPU 的机制。所以,这里的中断升高还是因为过多任务的调度问题,跟前面上下文切换次数的分析结果是一致的。

                      小结

                      现在再回到最初的问题,每秒上下文切换多少次才算正常呢?这个要看具体情况:如果系统的上下文切换次数比较稳定,那么从数百到一万以内,都应该算是正常的。但当上下文切换次数超过一万次,就可能已经出现了性能问题,这个时候我们可以借助 vmstatpidstat/proc/interrupts 等工具,来辅助排查性能问题的根源。

                      文章知识点与官方知识档案匹配,可进一步学习相关知识
                      CS入门技能树Linux入门初识Linux31175 人正在系统学习中

                      与[转帖]Linux性能分析(二):理解CPU上下文切换相似的内容:

                      [转帖]Linux性能分析(二):理解CPU上下文切换

                      在计算机中,上下文切换是指存储进程或线程的状态,以便以后可以还原它并从同一点恢复执行。这允许多个进程共享一个CPU,这是多任务操作系统的基本功能。 Linux 是一个多任务操作系统,它支持远大于 CPU 数量的任务同时运行,这依赖于CPU上下文切换。CPU 上下文切换,就是先把前一个任务的 CPU

                      [转帖]Linux性能分析:理解系统平均负载

                      Linux系统中,平均负载是指单位时间内,处于可运行状态和不可中断状态的进程数。它不仅包括了正在使用CPU的进程,也包括处于不可打断的睡眠状态的进程—它们是在等待其它系统资源如磁盘 I/O 等的进程。而CPU使用率,是单位时间内CPU繁忙情况的统计,跟平均负载并不一定完全对应。 有诸多方式监测系统平

                      [转帖]Linux性能优化(五)——性能监控工具

                      原文 https://blog.51cto.com/9291927/2594065 1、性能分析工具简介 Brendan Gregg是算机性能设计、分析和调优专家,编写开源大量性能测试工具。http://www.brendangregg.comnode 一、性能分析工具 二、性能观测工具 三、性能测

                      [转帖]【初探】java性能火焰图的生成

                      https://www.cnblogs.com/hama1993/p/10580581.html 一、前言 开始之前,你需要准备的环境: Linux系统机器或者虚拟机一台,里面需要安装的软件:git、jdk、perl。 二、简单介绍 java性能分析火焰图的所做的事情就是能够分析出java程序运行期

                      [转帖]【初探】java性能火焰图的生成

                      https://www.cnblogs.com/hama1993/p/10580581.html 一、前言 开始之前,你需要准备的环境: Linux系统机器或者虚拟机一台,里面需要安装的软件:git、jdk、perl。 二、简单介绍 java性能分析火焰图的所做的事情就是能够分析出java程序运行期

                      [转帖]linux 性能分析工具 perf + FlameGraph

                      https://www.cnblogs.com/lausaa/p/12098716.html 常用命令: perf record -e cpu-clock -g -p $pid sleep 10 perf script -i perf.data > perf.unfold /FlameGraph-m

                      [转帖]Linux性能优化(十)——CPU性能分析工具

                      Linux性能优化(十)——CPU性能分析工具 https://blog.51cto.com/u_9291927/2594169 一、CPU性能指标 1、CPU使用率 CPU使用率是最常见的一个性能指标,描述了非空闲时间占总CPU 时间的百分比,根据CPU上运行任务的不同,分为用户CPU、系统CPU

                      [转帖]Linux性能优化实战 —— CPU

                      https://www.jianshu.com/p/2b6658ad59b3 Linux性能分析概要 1. 性能指标 linux性能指标.png 随着应用负载的增加,系统资源的使用也会升高,甚至达到极限。而性能问题的本质,就是系统资源已经达到瓶颈,但请求的处理却还不够快,无法支撑更多的请求。性能分析

                      [转帖]Linux性能优化(四)——BCC性能监控工具

                      一、BCC简介 1、BCC简介 BCC是一个Python库,简化了eBPF应用的开发过程,并收集了大量性能分析相关的eBPF应用。BCC为BPF开发提供了不同的前端支持,包括Python和Lua,实现了map创建、代码编译、解析、注入等操作,使开发人员只需聚焦于用C语言开发要注入的内核代码。 BCC

                      [转帖]Linux性能优化(四)——BCC性能监控工具

                      原文 https://blog.51cto.com/9291927/2593705 1、BCC简介 一、BCC简介 BCC是一个Python库,简化了eBPF应用的开发过程,并收集了大量性能分析相关的eBPF应用。BCC为BPF开发提供了不一样的前端支持,包括Python和Lua,实现了map建立、