CPU上下文切换

CPU 上下文

当多个进程竞争同一个 CPU 的时候,会导致大量的上下文切换,从而使得系统负载升高。

常见的系统都是多任务操作系统,进程数量远大于 CPU 数量,系统会在很短的时间内将 CPU 资源轮流分给不同的进程使用,从而营造多任务的错觉。

CPU 知道其实并不用关心任务从从哪里开始,它只需要把 PC(程序计数器)中的指令取出来执行即可。

即将执行的下一条指令位置,寄存器等等这些速度极快的在 CPU 中的小内存被叫做 CPU 上下文

CPU 上下文切换

就是把前一个任务的的上下文保存起来,然后使用新的上下文覆盖当前上下文,等执行完当前任务或者时间片到了再切换回原来的上下文。

根据任务不同,分成不同的上下文切换:进程上下文切换,线程上下文切换以及中断上下文切换

进程上下文切换

正常执行的进程处于 Ring3 (用户空间),而内核空间是 Ring0,当进程尝试读写文件的时候,会先使用系统调用打开文件描述符,open() -> write() -> close() 等一系列操作,系统调用也是会是的 CPU 进行上下文切换的,通常涉及上下文切换的在以下场景:

  • 时间片耗尽
  • 进程系统资源不足
  • 进程通过sleep函数休眠
  • 高优先级进程运行

进程是由内核来管理调度的,进程的切换只能发生在内核态,所以进程的上下文切换不仅包括了虚拟内存、栈、全局变量等,还包括了内核堆栈,寄存器的让那个内核空间的状态。

线程上下文切换

线程和进程的区别?

  • 当进程只包含一个线程时,可以认为进程等于线程
  • 当进程拥有多个线程时,这些线程会共享相同的虚拟内存和全局全局变量。
  • 线程有私有数据,栈,寄存器等。

其实没有进程上下文切换,只有线程上下文……

因为线程是调度的单位,然而调度的过程中会发现两种不同的情况:

第一种,前后两个线程不属于同一个进程。此时,因为资源不共享,所以切换过程和进程一样。 第二种,前后两个线程属于同一个进程。此时,因为虚拟内存是共享的,所以在切换时,虚拟内存这些资源就保持不同,只需要切换线程的私有数据,寄存器等不共享的数据。

线程共享相同的虚拟内幕和全局变量等资源,这些资源在上下文切换时不需要修改。

中断上下文切换

对于同一个 CPU 来说,中断处理比进程拥有更高的优先级。

如何查看系统中断

vmstat 1 1 每隔1秒输出1次,总计输出1秒

1
2
3
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 2  0      0 269564  64872 890480    0    0   168    52  975 7756  6  9 86  0  0
  • r:是就绪队列,超过cpu核心数就会产生竞争
  • us和sy: user 和 system 列:表示 CPU 分别被 user 和 system 占用多少?
  • in:指标是975,说明每秒中断975次
  • cs:上下文切换 7756次

使用 pidstat 能看到进程详细信息

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
  ~ pidstat -wt  | head
Linux 4.19.0-11-amd64 (debian)  Friday, November 13, 2020       _x86_64_        (2 CPU)

09:22:09 HKT   UID      TGID       TID   cswch/s nvcswch/s  Command
09:22:09 HKT     0         1         -      0.41      0.36  systemd
09:22:09 HKT     0         -         1      0.41      0.36  |__systemd
09:22:09 HKT     0         2         -      0.05      0.00  kthreadd
09:22:09 HKT     0         -         2      0.05      0.00  |__kthreadd
09:22:09 HKT     0         3         -      0.00      0.00  rcu_gp
09:22:09 HKT     0         -         3      0.00      0.00  |__rcu_gp
09:22:09 HKT     0         4         -      0.00      0.00  rcu_par_gp
  • cswch 自愿上下文切换:进程无法获得需要资源而产生上下文切换。
  • nvcswch 非自愿上下文切换:进程时间片到了,被系统强制调度,而发生的上下文切换

更深层次查看进程运行情况

某个进程运行消耗大量资源,却不知道它在干什么,因此,希望使用 perf 工具来查看。

1
2
3
4
5
6
7
8
9
int add(int a, int b){
    return a + b;
}

int main(){
    while (1){
        add(1, 1);
    }
}

编译运行,CPU单线程占用100%,使用top获取到它的 pid 后,可以使用 perf 查看具体情况。

执行命令 sudo perf top -g -p 5444

可以从执行关系里看到 add 这个函数占用了 60% 的资源

Licensed under CC BY-NC-SA 4.0
Built with Hugo
Theme Stack designed by Jimmy