[转帖]Linux性能分析(二):理解CPU上下文切换
在计算机中,上下文切换是指存储进程或线程的状态,以便以后可以还原它并从同一点恢复执行。这允许多个进程共享一个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 上下文切换。不过,需要注意的是,系统调用过程中,并不会涉及到虚拟内存等进程用户态的资源,也不会切换进程。
查看系统上下文切换
我们通常使用vmstat和pidstat来查看系统上下文切换,先看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 时,就容易发生非自愿上下文切换。
实战案例
预先安装 sysbench 和 sysstat 包:
$ 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,us和 sy列CPU 使用率加起来上升到了 100%,中断in列也有2000多,看起来也是个问题。
那么到底是什么进程导致了这些问题呢?我们在第三个和第四个终端分别打开top和pidstat:
$ 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
从top和pidstat都可以看出CPU 使用率的升高果然是 sysbench 导致的,它的 CPU 使用率已经达到了 100%。另外可以看出sysbench的子线程的上下文切换次数非常多。
最后我们还要在新的中断查看下中断次数:
# -d 参数表示高亮显示变化的区域
$ watch -d cat /proc/interrupts
CPU0 CPU1
RES: 982564 1036295 Rescheduling interrupts
观察一段时间,你可以发现,变化速度最快的是重调度中断(RES),这个中断类型表示,唤醒空闲状态的 CPU 来调度新的任务运行。这是多处理器系统(SMP)中,调度器用来分散任务到不同 CPU 的机制。所以,这里的中断升高还是因为过多任务的调度问题,跟前面上下文切换次数的分析结果是一致的。
小结
现在再回到最初的问题,每秒上下文切换多少次才算正常呢?这个要看具体情况:如果系统的上下文切换次数比较稳定,那么从数百到一万以内,都应该算是正常的。但当上下文切换次数超过一万次,就可能已经出现了性能问题,这个时候我们可以借助 vmstat 、 pidstat 和 /proc/interrupts 等工具,来辅助排查性能问题的根源。
[转帖]Linux性能分析(二):理解CPU上下文切换的更多相关文章
- 一文掌握 Linux 性能分析之 CPU 篇
本文首发于我的公众号 Linux云计算网络(id: cloud_dev),专注于干货分享,号内有 10T 书籍和视频资源,后台回复「1024」即可领取,欢迎大家关注,二维码文末可以扫. 平常工作会涉及 ...
- 【转】一文掌握 Linux 性能分析之 CPU 篇
[转]一文掌握 Linux 性能分析之 CPU 篇 平常工作会涉及到一些 Linux 性能分析的问题,因此决定总结一下常用的一些性能分析手段,仅供参考. 说到性能分析,基本上就是 CPU.内存.磁盘 ...
- 【原创】一文掌握 Linux 性能分析之 I/O 篇
本文首发于我的公众号 Linux云计算网络(id: cloud_dev),专注于干货分享,号内有 10T 书籍和视频资源,后台回复「1024」即可领取,欢迎大家关注,二维码文末可以扫. 一文掌握 Li ...
- 如何理解CPU上下文切换(二)
如何理解CPU上下文切换(二) 1.引 你们好,可爱的小伙伴们.^_^ 多个进程竞争CPU就是一个经常被我们忽视的问题. 你们一定很好奇,进程在竞争CPU的时候并没有真正运行,为什么还会导致系统的负载 ...
- 【转】一文掌握 Linux 性能分析之 I/O 篇
[转]一文掌握 Linux 性能分析之 I/O 篇 这是 Linux 性能分析系列的第三篇,前两篇分别讲了 CPU 和 内存,本篇来看 IO. IO 和 存储密切相关,存储可以概括为磁盘,内存,缓存, ...
- 一文掌握 Linux 性能分析之网络篇(续)
本文首发于我的公众号 Linux云计算网络(id: cloud_dev),专注于干货分享,号内有 10T 书籍和视频资源,后台回复「1024」即可领取,欢迎大家关注,二维码文末可以扫. 这是 Linu ...
- 一文掌握 Linux 性能分析之网络篇
本文首发于我的公众号 Linux云计算网络(id: cloud_dev),专注于干货分享,号内有 10T 书籍和视频资源,后台回复「1024」即可领取,欢迎大家关注,二维码文末可以扫. 这是 Linu ...
- Linux 性能分析工具汇总合集
出于对Linux操作系统的兴趣,以及对底层知识的强烈欲望,因此整理了这篇文章.本文也可以作为检验基础知识的指标,另外文章涵盖了一个系统的方方面面.如果没有完善的计算机系统知识,网络知识和操作系统知识, ...
- [转]Linux性能分析工具汇总合集
出于对Linux操作系统的兴趣,以及对底层知识的强烈欲望,因此整理了这篇文章.本文也可以作为检验基础知识的指标,另外文章涵盖了一个系统的方方面面.如果没有完善的计算机系统知识,网络知识和操作系统知识, ...
- 超全整理!Linux性能分析工具汇总合集
转自:http://rdc.hundsun.com/portal/article/731.html?ref=myread 出于对Linux操作系统的兴趣,以及对底层知识的强烈欲望,因此整理了这篇文章. ...
随机推荐
- 宝塔面板如何用一IP不同端口创建不同的网站(“您添加的站点已存在”)
问题描述 玩宝塔面板的时候,一开始没有云服务器,需要在本地虚拟机里搭建各种网站,想在本地服务器下搭建多个站点,但是总会遇到"您添加的站点已存在"这个现象. 问题原因及解决办法 出现 ...
- 万万没想到,我在夜市地摊解决了MySQL临时表空间难题~~
都说"大隐隐于市,高手在深宫".突如其来的"摆地摊"风潮,让原本冷清的街道热闹非凡,也让众人发现了那些神龙见首不见尾的大神们. 这不,小毛在下班的途中就遇到了大 ...
- 让“物”能说会道,揭晓华为云IOT黑科技
什么是物联网?如何让"物"说话? 如今是一个万物互联的时代,物联网已经成为一个高大上的名词,那什么是物联网呢?从人与人之间的连接来看,指的是人们之间的通话.视频.进入智能时代以后, ...
- bucket表:数仓存算分离中CU与DN解绑的关键
摘要:Bucket存储是数据共享中重要的一环,当前阶段,bucket存储可以将列存中的CU数据和DN节点解绑. 本文分享自华为云社区<存算分离之bucket表--[玩转PB级数仓GaussDB( ...
- 从原生迈向混合,小而美团队如何搞定APP高效定制
摘要:洞悉华为云数字化差旅App的架构变迁之路,体验混合开发魅力. 本文分享自华为云社区<DTSE Tech Talk 第21期丨从原生迈向混合,小而美团队如何搞定APP高效定制?>, ...
- 鱼和熊掌兼得:C++代码在编译时完成白盒测试
摘要:如果能够让代码在编译的时候,自动完成白盒测试,这不是天方夜谭. 白盒测试也叫开发者测试,是对特定代码函数或模块所进行的功能测试.当前主流的白盒测试方法是:先针对仿真或者生产环境编译出可执行文件, ...
- 总结vue3 的一些知识点:Vue.js 条件语句
Vue.js 条件语句 条件判断 v-if 条件判断使用 v-if 指令: v-if 指令 在元素 和 template 中使用 v-if 指令: <div id="app" ...
- webpack 从 0 到 1 构建 vue
前端领域框架百花齐放,各种优秀框架出现(react,Vue,ag)等等框架.为了方便开发者快速开发, 开发对应的 cli 脚手架,来提高产出.然而初中级的前端工程师对项目里的 webpack 封装和配 ...
- 服务器上TIME_WAIT过多怎么处理
正常情况下,TIME_WAIT是需要存在的 为了保证客户端发送的最后一个ACK报文能够到达服务器,因为这个ACK可能丢失,从而导致处在LAST-ACK状态的服务器收不到对FIN-ACK的确认报文,服务 ...
- COOIS增强
一.订单抬头增强 二.结构添加字段 添加ZGCWL字段 三.BADI增强 四.其他界面增强 不同的界面,选择不同的修改结构 抬头 ct_ioheader 组件 ct_ioopcomp 工序 ct_io ...