Linux系统优化之二:cpu上下文切换

linux是一个多任务操作系统,它支持远大于cpu核心数的任务同时运行,当然,这些任务实际上并不是真正同时运行,操作系统在很短时间内,将cpu资源轮流分配给这些任务,造成多任务同时运行的错觉。每个任务运行前,cpu需要知道从哪里加载,从哪里开始运行,需要系统事先设置好cpu寄存器和程序计数器,这些都是cpu在运行任务必须依赖的环境,这些也称为cpu上下文。上下文切换,先把前一个任务的cpu上下文保存起来,然后加载新任务的上下文,运行新任务,这称为上下文切换。

cpu上下文

  • 根据任务不同,cpu上下文切换分为以下几个:

    • 进程上下文切换:

      • linux按照特权等级,把进程运行空间分为内核空间和用户空间,cpu特权等级分为ring0-ring3四个环,ring0是操心系统内核空间,具有最高权限,可以访问所有资源,ring3是用户空间,只能访问受限资源,不能直接访问内存等硬件,必须通过系统调用陷入内核,由内核访问特权资源
      • 进程可以在用户空间运行,称为进程的用户态,也可以在内核空间运行,称为进程内核态,用户态转换到内核态的转变,需要系统调用完成
      • 一次系统调用过程,其实发生两次cpu上下文切换
      • 进程上下文切换,是指从一个进程切换到另一个进程。系统调用过程中,一直是同一个进程在运行
    • 线程上下文切换:线程是调度的基本单位,进程是资源拥有的基本单位

      • 进程只有一个线程时,可认为进程就等于线程
      • 当进程拥有多个线程时,这些线程会共享内存和全局变量等资源,这些资源在上下文切换时是不需要修改的
      • 线程也有自己的私有数据,上下文切换时,这些资源需要保存的
      • 线程上下文切换分两种情况,一是前后两个线程属于不同进程,此时资源不共享,此时与进程上下文切换是一样的,第二种情况是两个线程属于同一个进程,此时有些资源是共享的,只需要切换线程私有数据,如栈和寄存器
    • 中断上下文切换:

      • 中断会打断进程的正常调度和执行,中断处理完,会把打断的进程恢复执行
      • 中断上下文并不涉及进程的用户态,中断上下文,只包括内核态内中断服务程序
      • 对于同一个cpu,中断比进程有更高优先级,故此,中断上下文与进程上下文切换不会同时发生
      • 中断上下文切换也需要消耗cpu,切换过多也会降低系统性能,这时候要去排查
  • 上下文切换需要一定时间,这时间很短,但如果上下文切换频繁时,导致cpu将大量时间用在上下文切换上,进而大大缩短真正运行进程时间,这也是导致cpu平均负载升高的一个重要因素

  • 什么时候会发生上下文切换?

    • 只有在进程调度的时候,才需要进行上下文切换,linux系统为每个cpu都维护了一个就绪队列,将活跃进程(正在运行和正在等待运行的进程)按优先级和等待cpu的时间排序,按调度算法,选择优先级高的进程来运行
  • cpu调度

    • 为保证进程调度,cpu时间被划分为一段段时间片,时间片分配给进程,当一个进程时间片耗尽了,就会被系统挂起,切换到其他等待的cpu的进程运行
    • 进程在系统资源不足时,要等资源满足后才可以运行,此时进程也会挂起,系统调度其他进程运行
    • 进程通过sleep函数将自己主动挂起时,自然也会重新调度
    • 当有更高优先级的进程需要运行时,当前进程也会被挂起,运行高优先级进程
    • 发生硬件中断,进程会被中断挂起,转而执行中断程序

由上面可以看出,,虽然同为上下文切换,同进程内的线程切换,要比多进程的切换消耗跟少的资源,这也正是多线程代替多进程的一个优势

如何查看系统上下文切换情况

vmstat命令是常用的工具,命令结果如下:

# 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 360340  14456 1251768    0    0    24    35   70   97  6  2 92   0  0
 0  0      0 360340  14456 1251804    0    0     0     0   96  165  0  0 100  0  0
 0  0      0 360276  14456 1251804    0    0     0    63   94  155  0  0 100  0  0

 cs: 每秒上下文切换次数
 in:每秒中断次数
 r:就绪队列长度,就是正在运行和等待cpu进程数
 b:处于不可中断睡眠状态的进程数

vmstat只给出系统总体上下文切换情况,要看每个进程情况,要使用pidstat

# pidstat -w 5
08:30:36 PM   UID       PID   cswch/s nvcswch/s  Command
08:30:41 PM     0         8     12.75      0.00  rcu_sched
08:30:41 PM     0        11      0.20      0.00  watchdog/0
08:30:41 PM     0        14      0.20      0.00  watchdog/1
08:30:41 PM     0       321      0.20      0.00  kworker/0:1H
08:30:41 PM     0       459     17.73      0.00  xfsaild/sda4
cswch/s:每秒自愿上下文切换次数
nvcswch/s:每秒非自愿上下文切换次数
自愿上下文切换:进程无法获取所需自愿,导致的上下文切换,如:i/o,内存等系统资源不足,会发生自愿上下文切换
非自愿上下文切换:由于进程时间片已到,被系统强制调度,进而发生上下文切换,如大量进程争抢cpu,就容易发生非自愿上下文切换

案例分析

使用sysbench模拟多线程切换调度情况,使用vmstat和pidstat工具进行分析,开启三个终端
第一个终端上:

# sysbench --threads=10 --max-time=300 threads run

第二个终端上:


# 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
 8  0      0 350592  14456 1253500    0    0    22    34   72  798    6  2  92  0  0
 7  0      0 350592  14456 1253500    0    0     0     0 5701 1538551 11 89  0  0  0
 7  0      0 350592  14456 1253500    0    0     0     0 5674 1619777 15 86  0  0  0
 7  0      0 350592  14456 1253500    0    0     0     0 5197 1563957 14 86  0  0  0
 7  0      0 350592  14456 1253500    0    0     0     0 4767 1600961 14 86  0  0  0

从终端2可以看出,cs(上下文切换)上升到153万,r(就绪队列)达到7,远超cpu个数2,所以会有大量cpu竞争,us和sy加起来达到100%,系统(sy)cpu占用率达到86%,说明cpu主要被系统(内核)占用,中断次数(in)达到5701,综上所述,系统的就绪队列过长,达到7,远大于cpu个数2,这会导致大量上下文切换,大量切换进而导致cpu系统占用过高,但到底是哪个进程导致的,需要进行下一步分析
中断次数达到5700,要查看中断情况,需要查看/proc/interrupts,如下所示,变化最快的是RES,达到230多万,这中断类型是重调度中断,这种类型表示,唤醒空闲状态cpu来调度新任务运行,在多处理器系统中,调度器用来分散任务到不同cpu机制,通常也被称为处理器中断

查看中断情况:

# cat /proc/interrupts
....         CPU0       CPU1
NMI:          0          0   Non-maskable interrupts
 LOC:    1490665    1283555   Local timer interrupts
 SPU:          0          0   Spurious interrupts
 PMI:          0          0   Performance monitoring interrupts
 IWI:          0          0   IRQ work interrupts
 RTR:          0          0   APIC ICR read retries
 RES:    2306244    2366613   Rescheduling interrupts
 CAL:       3081       3575   Function call interrupts
 TLB:        161        241   TLB shootdowns
 TRM:          0          0   Thermal event interrupts
 THR:          0          0   Threshold APIC interrupts
 DFR:          0          0   Deferred Error APIC interrupts
 MCE:          0          0   Machine check exceptions
 MCP:         71         72   Machine check polls
 HYP:          0          0   Hypervisor callback interrupts
 ERR:          0
 MIS:          0
 PIN:          0          0   Posted-interrupt notification event
 NPI:          0          0   Nested posted-interrupt event
 PIW:          0          0   Posted-interrupt wakeup event

在第三个终端上,使用pidstat观察

# pidstat -w -u 1
08:45:20 PM   UID       PID    %usr %system  %guest   %wait    %CPU   CPU  Command
08:45:21 PM     0     27328    0.00    1.00    0.00    0.00    1.00     0  kworker/u256:1
08:45:21 PM     0     27491    0.00    1.00    0.00    0.00    1.00     1  sshd
08:45:21 PM     0     27761   28.00  100.00    0.00    0.00  100.00     1  sysbench
08:45:21 PM     0     27793    0.00    1.00    0.00    3.00    1.00     1  pidstat

08:45:20 PM   UID       PID   cswch/s nvcswch/s  Command
08:45:21 PM     0         8     19.00      0.00  rcu_sched
08:45:21 PM     0        16      2.00      0.00  ksoftirqd/1
08:45:21 PM     0       459     18.00      0.00  xfsaild/sda4
08:45:21 PM     0       983     10.00      0.00  vmtoolsd
08:45:21 PM   115      1408      1.00      0.00  snmpd
08:45:21 PM     0     27328    242.00      0.00  kworker/u256:1
08:45:21 PM     0     27400      6.00      0.00  kworker/0:0
08:45:21 PM     0     27491    128.00      1.00  sshd
08:45:21 PM     0     27772      3.00      0.00  kworker/1:0
08:45:21 PM     0     27793      1.00    237.00  pidstat

从pidstat输出可以看出,sysbench的cpu系统占用率(%system)达到100%,上下文切换来自其他进程,非自愿上下文切换(nvcswch)最高的是pidstat,达到237,自愿上下文切换(cswch)最高的是kworker和sshd

pidstat默认显示进程指标数据,若要显示线程数据,要使用-t选项,所以第三个终端命令建议使用:

# pidstat -wt -u 1

上下文切换啥情况才算正常

  • 主要取决于系统本身的cpu性能,如果系统上下文切换次数稳定,从几百到一万以内,但如果次数超过一万,或者次数出现数量级增长,就可能出现性能问题
  • 需要根据上下文切换类型,做具体分析
    • 自愿上下文切换变多,说明进程在等待资源,可能发生io问题
    • 非自愿上下文切换变多,说明进程被强制调度,也就是在争cpu,说明cpu成为瓶颈
    • 中断次数变多,说明cpu被中断程序占用,还需要查看/proc/interrups文件来分析中断类型

文章作者: BY 木易杨
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 BY 木易杨 !
评论
 上一篇
Linux系统优化之四:cpu使用率高,为啥找不到元凶? Linux系统优化之四:cpu使用率高,为啥找不到元凶?
当你发现系统cpu使用率很高的时候,不一定能找到相对应的高cpu使用率进程,本文就是讨论这样问题的,以一个实例逐步分析,给出解决思路 案例实验环境说明 以nginx+php服务为例,使用docker跑对应的服务 案例操作及分析过程#
2020-04-26 BY 木易杨
下一篇 
Linux系统优化之三:如何处理cpu占用100% Linux系统优化之三:如何处理cpu占用100%
cpu使用率linux通过proc这个虚拟文件系统,提供系统内部信息,/proc/stat提供的是系统cpu信息,可以使用以下命令查看: # cat /proc/stat |grep ^cpu cpu 241187 1658 242572
2020-04-25 BY 木易杨