理解linux的CPU上下文切换

前言

linux是一个多任务操作系统,它支持远大于CPU数量的任务同时运行。当然,这个同时运行不是真的同时运行,而是系统在很短的时间内轮流分配CPU资源,由于CPU的速度很快,所以给人一种同时运行的错觉。

每个任务运行前,CPU需要知道任务从哪加载、从哪开始运行,也就是需要系统设置好任务的CPU寄存器和程序计数器。这俩是CPU执行任何任务前所必须的依赖环境,因此也被叫做CPU上下文。而CPU上下文切换便是,先把前一个任务的CPU上下文保存起来,然后加载新任务的上下文到CPU寄存器和程序计数器中,最后再跳转到程序计数器所指向的新位置来运行新任务。

根据任务的不同,CPU的上下文切换可以分为几个不同的场景:进程上下文切换、线程上下文切换,以及中断上下文切换。

进程上下文切换

linux进程的运行空间分为内核空间和用户空间。内核空间具有最高权限,可以访问所有资源;用户空间只能访问受限资源,不能直接访问内存等硬件设备,必须通过系统调用陷入内核,才能访问特权资源。当进程在用户空间运行时,被称为进程的用户态;当进程陷入到内核空间,被称为进程的内核态。

用户态通过系统调用陷入内核态的时候,会发生CPU上下文切换。系统需要先保存用户态的上下文,然后加载内核态代码的上下文,执行完内核态任务后,再进行一次CPU上下文切换,切换回用户态的上下文。因此一次系统调用会发生两次CPU上下文切换。这里需要注意,系统调用过程一直是同一个进程在运行,不是一个进程切换到另一个进程。

进程是由内核管理和调度的,进程的切换只能在内核空间。因此,进程的上下文不仅包括虚拟内存、栈、全局变量等用户空间的资源,还包括了内核堆栈、寄存器等内核空间的状态。由于进程上下文较多,切换时候不仅包括用户态信息,还包括内核态信息,所以上下文切换时耗费的时间更多。虽然一般只需要纳秒级的时间,但进程上下文切换次数较多的时候,总体耗费的时间就比较可观了。在大量进程上下文切换的情况下,CPU将大量时间耗费在寄存器、内核栈、虚拟内存等资源的保存和恢复上,进而缩短了真正运行进程的时间。

Linux通过TLB(Translation Lookaside Buffer)来管理虚拟内存到物理内存的映射关系。当虚拟内存更新后,TLB也需要刷新,内存的访问也会随之变慢。

只有在进程调度的时候,才需要切换进程上下文。linux为每个CPU维护了一个就序队列,将活跃进程(在运行和等待中的进程)按照优先级和等待CPU的时间排序,然后选择最需要CPU的进程,也就是优先级最高和等待时间最长的进程来运行。

触发进程调度的常见场景:

  • 进程的CPU时间片用完,就会被系统挂起,切换到其他进程。
  • 进程所需的系统资源不足时,需要等到资源满足后才可以运行,这时候也会被挂起。
  • 进程通过sleep()函数主动挂起。
  • 有优先级更高的进程需要运行时,当前进程会被挂起。
  • 硬件中断时,进程会被挂起。

线程上下文切换

进程是资源分配的基本单位,线程是调度的基本单位。进程内有多个线程时,这些线程会共享相同的虚拟内存和全局变量等资源,这些资源在上下文切换时是不需要修改的。线程也有自己的私有资源,比如栈和寄存器等,这些在上下文切换时也需要保存。

  • 线程上下文切换前后的两个线程如果不属于同一个进程,此时资源不共享,切换过程同进程上下文切换。
  • 切换前后的两个线程若属于同一个进程,共享的虚拟内存等资源保持不动,切换线程的私有资源即可。在这种情况下,切换消耗的资源更少。

中断上下文切换

为了快速响应硬件的时间,中断处理会打断进程的正常调度和执行,转而调用中断处理程序。

中断上下文切换并不涉及进程的用户态,当中断发生在一个正处于用户态的进程时,不需要保存和恢复这个进程的用户态资源。中断上下文值包括内核态中断服务程序执行所必需的状态,包括CPU寄存器、内核堆栈、硬件中断参数等。

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

使用vmstat(apt或yum安装sysstat)可查看系统的上下文切换情况

# 每2秒输出一组报告,共1份报告
vmstat 2 1
procs  -----------memory----------  ---swap--  -----io----  -system--  ------cpu-----
r      b                            交换       空闲         缓冲       缓存            si  so  bi   bo   in   cs   us  sy  id  wa  st
1      0                            0          2118592      100844     1011392         0   0   211  259  188  249  3   1   96  0   0
  • cs(context switch):每秒上下文切换的次数。此处为249
  • in(interrupt): 每秒中断的次数。此处为188
  • r(running or runnable): 就绪队列的长度,即正在运行和等待CPU的进程数
  • b(blocked): 处于不可中断睡眠状态的进程数

查看每个进程的上下文切换情况

使用pidstat(同样依赖于sysstat)可查看进程的上下文切换情况。

# 每3秒输出一组报告,-w表示输出进程的上下文切换指标,-wt表示输出线程的上下文切换指标
pidstat -w 3

13时53分56秒   UID       PID   cswch/s nvcswch/s  Command
13时53分59秒     0         7      4.95      0.00  kworker/0:1-ata_sff
13时53分59秒     0        13      2.64      0.00  rcu_sched
13时53分59秒     0        14      0.33      0.00  migration/0
13时53分59秒     0        19      0.33      0.00  migration/1
13时53分59秒     0        31      1.98      0.00  kcompactd0
13时53分59秒     0       122      1.32      0.00  kworker/1:1H-kblockd
13时53分59秒     0       247      0.99      0.33  jbd2/dm-0-8
13时53分59秒   116       501      0.66      0.00  avahi-daemon
13时53分59秒     0       505      0.99      0.00  NetworkManager
13时53分59秒   104       541      0.33      0.00  rsyslogd
13时53分59秒     0       549      0.33      0.00  wpa_supplicant
13时53分59秒     0      5102      2.31      0.33  kworker/1:1-events
13时53分59秒     0      5576      1.98      0.00  kworker/u4:0-events_freezable_power_
13时53分59秒  1000      5707      0.33      0.00  pidstat
  • cswch: 每秒自愿上下文切换的次数(资源不足时,进程自愿上下文切换)
  • nvcswch: 每秒非自愿上下文切换的次数(时间片到期等原因,被系统强制调度而发生的上下文切换)

查看系统中断情况

# 持续查看/proc/interrupts, 并高亮变化的地方
watch -d cat /proc/interrupts

           CPU0       CPU1       
  0:         29          0   IO-APIC   2-edge      timer
  1:        602          0   IO-APIC   1-edge      i8042
  8:          0          0   IO-APIC   8-edge      rtc0
  9:          0          0   IO-APIC   9-fasteoi   acpi
 12:          0        366   IO-APIC  12-edge      i8042
 14:          0          0   IO-APIC  14-edge      ata_piix
 15:          0       2563   IO-APIC  15-edge      ata_piix
 18:          0          2   IO-APIC  18-fasteoi   vboxvideo
 19:      13747      23341   IO-APIC  19-fasteoi   enp0s3
 20:       8310          0   IO-APIC  20-fasteoi   vboxguest
 21:      17084      18354   IO-APIC  21-fasteoi   ahci[0000:00:0d.0], snd_intel8x0
 22:         25          0   IO-APIC  22-fasteoi   ohci_hcd:usb1
NMI:          0          0   Non-maskable interrupts
LOC:     309756     154510   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:      95556      93683   Rescheduling interrupts
CAL:       7548       8726   Function call interrupts
TLB:       7126       6564   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:          9          9   Machine check polls
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
  • RES:重调度中断。表示唤醒空闲状态的CPU来调度新的任务运行。

参考

  • 极客时间 - linux性能优化实战
posted @ 2022-03-06 14:05  花酒锄作田  阅读(213)  评论(0编辑  收藏  举报