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

过多的上下文切换,会把 CPU 时间消耗在寄存器、内核栈以及虚拟内存等数据的保存和恢复上,缩短进程真正运行的时间,成了系统性能大幅下降的一个 元凶。

使用 vmstat 这个工具,来查询系统的上下文切换情况。

vmstat 是一个常用的系统性能分析工具,主要用来分析系统的内存使用情况,也常用来分析 CPU 上下文切换和中断的次数。

vmstat 的使用示例:

[root@doit ~]# vmstat 5
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
0 0 0 1068760 120384 612716 0 0 0 0 57 98 0 0 100 0 0 cs(context switch)是每秒上下文切换的次数。
in(interrupt)则是每秒中断的次数。
r(Running or Runnable)是就绪队列的长度,也就是正在运行和等待 CPU 的进程数。
b(Blocked)则是处于不可中断睡眠状态的进程数。 这个例子中的上下文切换次数 cs 是 98 次,而系统中断次数 in 则是 57 次,而就绪队列长度 r 和不可中断状态进程数 b 都是 0。

vmstat 只给出了系统总体的上下文切换情况,要想查看每个进程的详细情况,就需要使用我们前面提到过的 pidstat 了。给它加上 -w 选项,你就可以查看每个进程上下文切换的情况了。

每隔 5 秒输出 1 组数据
[root@doit ~]# pidstat -w 5
Linux 4.20.0-1.el7.elrepo.x86_64 (doit) 07/12/2019 _x86_64_ (2 CPU) 04:28:40 PM UID PID cswch/s nvcswch/s Command
04:28:45 PM 0 9 0.20 0.00 ksoftirqd/0
04:28:45 PM 0 10 2.00 0.00 rcu_sched
04:28:45 PM 0 11 0.40 0.00 migration/0
04:28:45 PM 0 15 0.40 0.00 migration/1
04:28:45 PM 0 27 0.20 0.00 khugepaged
04:28:45 PM 0 3086 0.20 0.00 haveged
04:28:45 PM 0 3104 9.98 0.00 qemu-ga
04:28:45 PM 0 3492 1.00 0.00 httpd
04:28:45 PM 0 6094 0.40 0.00 python
04:28:45 PM 0 29541 5.39 0.00 kworker/1:0-events_power_efficient
04:28:45 PM 0 29590 1.40 0.00 kworker/0:1-events
04:28:45 PM 0 29716 0.20 0.20 pidstat 一个是 cswch ,表示每秒自愿上下文切换(voluntary context switches)的次数,另一个则是 nvcswch ,表示每秒非自愿上下文切换(non voluntary context switches)的次数。

所谓自愿上下文切换,是指进程无法获取所需资源,导致的上下文切换。比如说, I/O、内存等系统资源不足时,就会发生自愿上下文切换。

而非自愿上下文切换,则是指进程由于时间片已到等原因,被系统强制调度,进而发生的上下文切换。比如说,大量进程都在争抢 CPU 时,就容易发生非自愿上下文切换。

案例分析

上下文切换频率是多少次才算正常呢?

sysbench  是一个多线程的基准测试工具,一般用来评估不同系统参数下的数据库负载情况。当然,在这次案例中,我们只把它当成一个异常进程来看,作用是模拟上下文切换过多的问题。

预先安装 sysbench 和 sysstat 包,如 apt install sysbench sysstat

操作和分析

首先,在第一个终端里运行 sysbench ,模拟系统多线程调度的瓶颈:

# 以 10 个线程运行 5 分钟的基准测试,模拟多线程切换的问题
[root@doit ~]# sysbench --threads=10 --max-time=300 threads run
WARNING: --max-time is deprecated, use --time instead
sysbench 1.0.17 (using system LuaJIT 2.0.4) Running the test with following options:
Number of threads: 10
Initializing random number generator from current time Initializing worker threads... Threads started!

在第二个终端运行 vmstat ,观察上下文切换情况:

[root@doit ~]# vmstat 1
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
6 0 0 1066924 120388 612752 0 0 0 0 7 3 0 0 100 0 0
8 0 0 1066924 120388 612768 0 0 0 0 2472 1695514 17 82 1 0 0
8 0 0 1066924 120388 612768 0 0 0 0 3062 1847851 17 83 1 0 0
7 0 0 1066924 120388 612768 0 0 0 0 2093 1696907 16 82 2 0 0
cs 列的上下文切换次数从之前的 3 骤然上升到了 169 万。同时,注意观察其他几个指标:
r 列:就绪队列的长度已经到了 8,远远超过了系统 CPU 的个数 2,所以肯定会有大量的 CPU 竞争。
us(user)和 sy(system)列:这两列的 CPU 使用率加起来上升到了 100%,其中系统 CPU 使用率,也就是 sy 列高达 83%,说明 CPU 主要是被内核占用了。
in 列:中断次数也上升到了 1 万左右,说明中断处理也是个潜在的问题。

综合这几个指标,我们可以知道,系统的就绪队列过长,也就是正在运行和等待 CPU 的进程数过多,导致了大量的上下文切换,而上下文切换又导致了系统 CPU 的占用率升高。

那么到底是什么进程导致了这些问题呢?

在第三个终端再用 pidstat 来看一下, CPU 和进程上下文切换的情况

# 每隔 1 秒输出 1 组数据
-w 参数表示输出进程切换指标,而 -u 参数则表示输出 CPU 使用指标 [root@doit ~]# pidstat -w -u 1
Linux 4.20.0-1.el7.elrepo.x86_64 (doit) 07/12/2019 _x86_64_ (2 CPU) 04:38:35 PM UID PID %usr %system %guest %wait %CPU CPU Command
04:38:36 PM 0 29730 33.00 167.00 0.00 0.00 200.00 0 sysbench 04:38:35 PM UID PID cswch/s nvcswch/s Command
04:38:36 PM 0 10 3.00 0.00 rcu_sched
04:38:36 PM 0 16 1.00 0.00 ksoftirqd/1
04:38:36 PM 0 3104 10.00 0.00 qemu-ga
04:38:36 PM 0 3492 1.00 0.00 httpd
04:38:36 PM 0 29541 14.00 0.00 kworker/1:0-events_power_efficient
04:38:36 PM 0 29590 2.00 0.00 kworker/0:1-mm_percpu_wq
04:38:36 PM 0 29640 1.00 0.00 sshd
04:38:36 PM 0 29714 1.00 0.00 kworker/u4:0-events_unbound
04:38:36 PM 0 29742 1.00 0.00 pidstat
从 pidstat 的输出你可以发现,CPU 使用率的升高果然是 sysbench 导致的,它的 CPU 使用率已经达到了 200%。但上下文切换则是来自其他进程,自愿上下文切换频率最高的内核线程 kworker。

pidstat 输出的上下文切换次数,加起来也就几百,比 vmstat 的 139 万明显小了太多。这是怎么回事呢?难道是工具本身出了错吗?

pidstat 默认显示进程的指标数据,加上 -t 参数后, 才会输出线程的指标。

[root@doit ~]# pidstat -wt 1
Linux 4.20.0-1.el7.elrepo.x86_64 (doit) 07/12/2019 _x86_64_ (2 CPU) 04:45:02 PM UID TGID TID cswch/s nvcswch/s Command
04:45:03 PM 0 9 - 2.91 0.00 ksoftirqd/0
04:45:03 PM 0 - 9 2.91 0.00 |__ksoftirqd/0
04:45:03 PM 0 10 - 10.68 0.00 rcu_sched
04:45:03 PM 0 - 10 10.68 0.00 |__rcu_sched
04:45:03 PM 0 16 - 0.97 0.00 ksoftirqd/1
04:45:03 PM 0 - 16 0.97 0.00 |__ksoftirqd/1
04:45:03 PM 0 3104 - 9.71 0.00 qemu-ga
04:45:03 PM 0 - 3104 9.71 0.00 |__qemu-ga
04:45:03 PM 0 - 3820 0.97 0.00 |__tuned
04:45:03 PM 0 6094 - 0.97 0.00 python
04:45:03 PM 0 - 6094 0.97 0.00 |__python
04:45:03 PM 0 - 14238 0.97 0.00 |__node
04:45:03 PM 0 29590 - 1.94 0.00 kworker/0:1-mm_percpu_wq
04:45:03 PM 0 - 29590 1.94 0.00 |__kworker/0:1-mm_percpu_wq
04:45:03 PM 0 - 29768 20572.82 100301.94 |__sysbench
04:45:03 PM 0 - 29769 18718.45 110695.15 |__sysbench
04:45:03 PM 0 - 29770 22306.80 104282.52 |__sysbench
04:45:03 PM 0 - 29771 26055.34 86372.82 |__sysbench
04:45:03 PM 0 - 29772 20498.06 102583.50 |__sysbench
04:45:03 PM 0 - 29773 14956.31 81900.00 |__sysbench
04:45:03 PM 0 - 29774 29092.23 112430.10 |__sysbench
04:45:03 PM 0 - 29775 24825.24 110958.25 |__sysbench
04:45:03 PM 0 - 29776 23862.14 95394.17 |__sysbench
04:45:03 PM 0 - 29777 21125.24 87931.07 |__sysbench
04:45:03 PM 0 29780 - 12.62 0.00 kworker/1:1-events_power_efficient
04:45:03 PM 0 - 29780 12.62 0.00 |__kworker/1:1-events_power_efficient
04:45:03 PM 0 29783 - 0.97 1.94 pidstat
04:45:03 PM 0 - 29783 0.97 1.94 |__pidstat

虽然 sysbench 进程(也就是主线程)的上下文切换次数看起来并不多,但它的子线程的上下文切换次数却有很多。看来,上下文切换罪魁祸首,还是过多的sysbench 线程。
   在观察系统指标时,除了上下文切换频率骤然升高,还有一个指标也有很大的变化。是的,正是中断次数。中断次数也上升到了 1 万,但到底是什么类型的中断上升了,现在还不清楚。接下来继续抽丝剥茧找源头。既然是中断,我们都知道,它只发生在内核态,而 pidstat 只是一个进程的性能分析工具,并不提供任何关于中断的详细信息,怎样才能知道中断发生的类型呢?

没错,那就是从 /proc/interrupts 这个只读文件中读取。/proc 实际上是 Linux 的一个虚拟文件系统,用于内核空间与用户空间之间的通信。/proc/interrupts  就是这种通信机制的一部分,提供了一个只读的中断使用情况。

[root@doit ~]# watch -d cat /proc/interrupts

Every 2.0s: cat /proc/interrupts                                         Fri Jul 12 16:49:08 2019

           CPU0       CPU1

 LOC:   83008310  107586288   Local timer interrupts
SPU: 0 0 Spurious interrupts
PMI: 0 0 Performance monitoring interrupts
IWI: 0 1 IRQ work interrupts
RTR: 0 0 APIC ICR read retries
RES: 9169892 7079828 Rescheduling interrupts

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

现在再回到最初的问题,每秒上下文切换多少次才算正常呢?
     这个数值其实取决于系统本身的 CPU 性能。在我看来,如果系统的上下文切换次数比较稳定,那么从数百到一万以内,都应该算是正常的。但当上下文切换次数超过一万次,或者切换次数出现数量级的增长时,就很可能已经出现了性能问题。

这时,你还需要根据上下文切换的类型,再做具体分析。比方说:

自愿上下文切换变多了,说明进程都在等待资源,有可能发生了 I/O 等其他问题;
    非自愿上下文切换变多了,说明进程都在被强制调度,也就是都在争抢 CPU,说明 CPU 的确成了瓶颈;
    中断次数变多了,说明 CPU 被中断处理程序占用,还需要通过查看 /proc/interrupts 文件来分析具体的中断类型。

小结

通过一个 sysbench 的案例,给你讲了上下文切换问题的分析思路。碰到上下文切换次数过多的问题时,我们可以借助 vmstat 、 pidstat 和 /proc/interrupts 等工具,来辅助排查性能问题的根源。

首先通过uptime查看系统负载,然后使用mpstat结合pidstat来初步判断到底是cpu计算量大还是进程争抢过大或者是io过多,接着使用vmstat分析切换次数,以及切换类型,来进一步判断到底是io过多导致问题还是进程争抢激烈导致问题。

2.2 CPU 上下文切换是什么意思?(下)的更多相关文章

  1. cpu上下文切换(下)

    --怎么查看系统的上下文切换情况 过多的上下文切换,会把cpu时间消耗在寄存器.内核栈以及虚拟内存等数据的保存和恢复上,缩短进程真正运行的时间,成了系统性能大幅下降的一个元凶. 查看,使用vmstat ...

  2. 04 | 基础篇:经常说的 CPU 上下文切换是什么意思?(下)

    上一节,我给你讲了 CPU 上下文切换的工作原理.简单回顾一下,CPU 上下文切换是保证 Linux 系统正常工作的一个核心功能,按照不同场景,可以分为进程上下文切换.线程上下文切换和中断上下文切换. ...

  3. 04讲基础篇:经常说的CPU上下文切换是什么意思(下)

    具体分析 自愿上下文切换变多了,说明进程都在等待资源,有可能发生了 I/O 等其他问题: 非自愿上下文切换变多了,说明进程都在被强制调度,也就是都在争抢 CPU,说明 CPU 的确成了瓶颈: 中断次数 ...

  4. 【转】CPU上下文切换的次数和时间(context switch)

    http://iamzhongyong.iteye.com/blog/1895728 什么是CPU上下文切换? 现在linux是大多基于抢占式,CPU给每个任务一定的服务时间,当时间片轮转的时候,需要 ...

  5. CPU上下文切换详解

    CPU上下文切换详解 原文地址,译文地址,译者: 董明鑫,校对:郑旭东 上下文切换(有时也称做进程切换或任务切换)是指CPU 从一个进程或线程切换到另一个进程或线程.进程(有时候也称做任务)是指一个程 ...

  6. CPU上下文切换的次数和时间(context switch)

    什么是CPU上下文切换? 现在linux是大多基于抢占式,CPU给每个任务一定的服务时间,当时间片轮转的时候,需要把当前状态保存下来,同时加载下一个任务,这个过程叫做上下文切换.时间片轮转的方式,使得 ...

  7. CPU 上下文切换及案例分析

    什么是CPU 上下文 我们都知道,Linux是一个多任务操作系统,它远支持大于CPU数量的任务同时运行,当然,这些任务实际上并不是真的在同时运行,而是因为系统在很短时间内,将CPU轮流分配给他们,造成 ...

  8. Linux性能优化从入门到实战:03 CPU篇:CPU上下文切换

      linux操作系统是将CPU轮流分配给任务,分时执行的.而每次执行任务时,CPU需要知道CPU寄存器(CPU内置的内存)和程序计数器PC(CPU正在执行指令和下一条指令的位置)值,这些值是CPU执 ...

  9. 03 | 基础篇:经常说的 CPU 上下文切换是什么意思?(上)

    上一节,我给你讲了要怎么理解平均负载( Load Average),并用三个案例展示了不同场景下平均负载升高的分析方法.这其中,多个进程竞争 CPU 就是一个经常被我们忽视的问题. 我想你一定很好奇, ...

随机推荐

  1. (十三)Docker容器进入的4种方式

    简介 在使用Docker创建了容器之后,大家比较关心的就是如何进入该容器了,其实进入Docker容器有好几多种方式,这里我们就讲一下常用的几种进入Docker容器的方法. 进入Docker容器比较常见 ...

  2. 《SQL必知必会》学习笔记整理

    简介 本笔记目前已包含 <SQL必知必会>中的所有章节. 我在整理笔记时所考虑的是:在笔记记完后,当我需要查找某个知识点时,不需要到书中去找,只需查看笔记即可找到相关知识点.因此在整理笔记 ...

  3. 1038 Recover the Smallest Number

    Given a collection of number segments, you are supposed to recover the smallest number from them. Fo ...

  4. 789. Escape The Ghosts

    You are playing a simplified Pacman game. You start at the point (0, 0), and your destination is (ta ...

  5. Spring Security极简入门三部曲(上篇)

    目录 Spring Security极简入门三部曲(上篇) 写在前面 为什么要用Spring Security 数据库设计 demo时刻 核心代码讲解 小结 Spring Security极简入门三部 ...

  6. 5-tcp套接字服务端编程

    import socket 1.创建套接字 sockfd= socket.socket(socket_family = AF_INIT,socket_type=SOCK_STREAM,proto) 功 ...

  7. ThnikPHP3.2 学习链接整理

    ThnikPHP3.2 学习链接整理 ThinkPHP3.2.3 U()方法的使用总结 看云手册 ThinkPHP3.2完全开发手册 TP3.2单字母函数 TP3.x中 M方法和D方法的区别

  8. PHP程序员要掌握哪些技术

    PHP程序员要掌握哪些技术 第一阶段: (PHP+MySQL核心编程)面向对象编程MySQL数据库,MySQL的优化细节.HTTP协议,http也是我们web开发的基石.对我们了解PHP底层机制有很大 ...

  9. hdu4560 不错的建图,二分最大流

    题意: 我是歌手 Time Limit: 6000/2000 MS (Java/Others) Memory Limit: 65535/32768 K (Java/Others) Total Subm ...

  10. POJ2688状态压缩(可以+DFS剪枝)

    题意:       给你一个n*m的格子,然后给你一个起点,让你遍历所有的垃圾,就是终点不唯一,问你最小路径是多少? 思路:       水题,方法比较多,最省事的就是直接就一个BFS状态压缩暴搜就行 ...