在计算机中,上下文切换是指存储进程或线程的状态,以便以后可以还原它并从同一点恢复执行。这允许多个进程共享一个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上下文切换的更多相关文章

  1. 一文掌握 Linux 性能分析之 CPU 篇

    本文首发于我的公众号 Linux云计算网络(id: cloud_dev),专注于干货分享,号内有 10T 书籍和视频资源,后台回复「1024」即可领取,欢迎大家关注,二维码文末可以扫. 平常工作会涉及 ...

  2. 【转】一文掌握 Linux 性能分析之 CPU 篇

    [转]一文掌握 Linux 性能分析之 CPU 篇 平常工作会涉及到一些 Linux 性能分析的问题,因此决定总结一下常用的一些性能分析手段,仅供参考. 说到性能分析,基本上就是 CPU.内存.磁盘 ...

  3. 【原创】一文掌握 Linux 性能分析之 I/O 篇

    本文首发于我的公众号 Linux云计算网络(id: cloud_dev),专注于干货分享,号内有 10T 书籍和视频资源,后台回复「1024」即可领取,欢迎大家关注,二维码文末可以扫. 一文掌握 Li ...

  4. 如何理解CPU上下文切换(二)

    如何理解CPU上下文切换(二) 1.引 你们好,可爱的小伙伴们.^_^ 多个进程竞争CPU就是一个经常被我们忽视的问题. 你们一定很好奇,进程在竞争CPU的时候并没有真正运行,为什么还会导致系统的负载 ...

  5. 【转】一文掌握 Linux 性能分析之 I/O 篇

    [转]一文掌握 Linux 性能分析之 I/O 篇 这是 Linux 性能分析系列的第三篇,前两篇分别讲了 CPU 和 内存,本篇来看 IO. IO 和 存储密切相关,存储可以概括为磁盘,内存,缓存, ...

  6. 一文掌握 Linux 性能分析之网络篇(续)

    本文首发于我的公众号 Linux云计算网络(id: cloud_dev),专注于干货分享,号内有 10T 书籍和视频资源,后台回复「1024」即可领取,欢迎大家关注,二维码文末可以扫. 这是 Linu ...

  7. 一文掌握 Linux 性能分析之网络篇

    本文首发于我的公众号 Linux云计算网络(id: cloud_dev),专注于干货分享,号内有 10T 书籍和视频资源,后台回复「1024」即可领取,欢迎大家关注,二维码文末可以扫. 这是 Linu ...

  8. Linux 性能分析工具汇总合集

    出于对Linux操作系统的兴趣,以及对底层知识的强烈欲望,因此整理了这篇文章.本文也可以作为检验基础知识的指标,另外文章涵盖了一个系统的方方面面.如果没有完善的计算机系统知识,网络知识和操作系统知识, ...

  9. [转]Linux性能分析工具汇总合集

    出于对Linux操作系统的兴趣,以及对底层知识的强烈欲望,因此整理了这篇文章.本文也可以作为检验基础知识的指标,另外文章涵盖了一个系统的方方面面.如果没有完善的计算机系统知识,网络知识和操作系统知识, ...

  10. 超全整理!Linux性能分析工具汇总合集

    转自:http://rdc.hundsun.com/portal/article/731.html?ref=myread 出于对Linux操作系统的兴趣,以及对底层知识的强烈欲望,因此整理了这篇文章. ...

随机推荐

  1. 神经网络基础篇:史上最详细_详解计算图(Computation Graph)

    计算图 可以说,一个神经网络的计算,都是按照前向或反向传播过程组织的.首先计算出一个新的网络的输出(前向过程),紧接着进行一个反向传输操作.后者用来计算出对应的梯度或导数.计算图解释了为什么用这种方式 ...

  2. 十问Huawei Cloud Toolkit:开发插件如何提升云上开发效能

    本文分享自华为云社区<[云享问答]第2期 十问Huawei Cloud Toolkit:开发插件如何提升云上开发效能>,作者:华为云社区精选. 众所周知,桌面集成开发环境(IDE)已经融入 ...

  3. 基于DAYU的实时作业开发,分分钟搭建企业个性化推荐平台

    摘要:搭建这个平台最费时耗力的事莫过于对批.流作业的编排,作业组织管理以及任务调度了.但是这一切,用DAYU的数据开发功能几个任务可通通搞定. 大多数电商类企业都会搭建自己的个性化推荐系统,利用自己拥 ...

  4. 代码也能“杀”虫:此虫,真虫非Bug也

    摘要:看这群大学生如何保护粮食,让害虫.霉变无处遁形. 国以民为本,民以食为天,对有着14亿人口的中国来说,粮食安全,一直都是关系国计民生的头等大事. 2010年以来,我国人均粮食占有量持续高于世界平 ...

  5. 撬动百亿VRAR产业,让VR们“造”起来

    摘要:四大亮点抢先看,12月28-29日不见不散! 随着5G商用的加速及元宇宙.数字人等概念的兴起,虚拟现实技术作为未来世界的入口,正受到越来越多的关注,也将成为驱动数字经济发展和产业转型升级的关键技 ...

  6. 下载安装Ipa Guard

    ​ 可以前往ipaguard工具官网下载,工具是免费下载,免费体验使用的.下载地址是https://www.ipaguard.com. 下载后解压工具便ok了,工具是绿色软件,无需其他安装流程.双击I ...

  7. Docker cp 将宿主机上的文件复制到容器中

    [root@localhost ~]# docker cp /opt/web/docker_cp.txt tomcat9093:/usr/local/apache-tomcat-9.0.31/ [ro ...

  8. Profile Config 多环境不同配置

    应用场景如:我们可以在开发.测试环境中,启用 Swagger,在生产环境中不启用 package com.vipsoft.web.boot.config; import springfox.docum ...

  9. docker centos 容器时间与宿主机时间不一致

    上图 容器时间不一致会造成N多问题,估计各位看官儿应该深有体会. 我处理的方式是在,dockerfile 中进行增加一条命令进行设置: RUN cp /usr/share/zoneinfo/Asia/ ...

  10. 图解 Promise 实现原理(四)—— Promise 静态方法实现

    本文首发于 vivo互联网技术 微信公众号 链接:https://mp.weixin.qq.com/s/Lp_5BXdpm7G29Z7zT_S-bQ作者:Morrain 了用法,原生提供了Promis ...