Linux 性能分析 | 上下文切换

416 阅读11分钟

引言

计算机科学中上下文指执行某个操作所需的相关状态。操作系统中的上下文切换是一种用于支持多任务处理的基本功能,允许单个处理器在多个进程或线程之间高效地切换,从而使计算机可以同时运行多个程序。上下文切换是必需的和有成本的,因此如何合理减少上下文切换和降低上下文切换的成本,是需要操作系统和开发者共同面对的问题。本文使用了vmstat工具,分别分析了进程、线程和协程上下文切换的成本。

上下文切换概述

进程上下文

进程是操作系统分配资源和调度的基本单位,拥有独立的地址空间和系统资源。一个进程的上下文包括:

  • 寄存器状态:包括程序计数器、堆栈指针、通用寄存器等。
  • 内存管理信息:如虚拟地址空间、页表等。
  • I/O状态信息:包括文件描述符、网络连接状态等。
  • 进程控制块 (PCB) :包含进程ID、进程状态(就绪、等待、运行)、调度优先级等。

线程上下文

线程是进程中的执行单元,线程间共享进程的地址空间和系统资源,但拥有自己的执行栈和寄存器集合。线程的上下文通常包括:

  • 寄存器状态:每个线程都有自己的寄存器集,包括程序计数器和堆栈指针。
  • 线程栈:每个线程都有自己的栈空间,用于存储执行过程中的局部变量、函数参数和返回地址。
  • 线程控制块 (TCB) :包含线程ID、线程状态、调度信息等。

协程上下文

协程是用户态的轻量级“线程”,由程序自行管理,不由操作系统内核调度。协程相比线程更加轻量,切换开销较小。协程的上下文包括:

  • 寄存器状态:程序计数器、通用寄存器等。协程通常使用的是线程的栈。
  • 协程栈:协程可能有自己的栈,尽管这个栈通常比线程栈要小。
  • 当前协程状态:记录了协程的当前状态,如运行、挂起等。
  • 调度器指针:指向管理这个协程的调度器或协程管理器。

使用多种工具分析上下文切换

vmstat(Virtual Memory Statistic)是一个监测虚拟内存、进程、CPU活动等系统性能的工具,在这里用来分析 CPU 上下文切换,在空闲状态下的使用示例如下

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 1792 3176396 1219380 19831540 0    0    0     0    1245 2580 0  0  100  0  0
  • procs (进程)

    • r: 运行队列中的进程数(即正在运行或等待CPU的进程数)
    • b: 不可中断睡眠状态的进程数(等待资源的进程)
  • memory (内存)

    • swpd: 交换空间总量
    • free: 空闲内存总量
    • buff: 作为缓冲使用的内存总量(磁盘级别)
    • cache: 作为缓存使用的内存总量(文件级别)
  • swap (交换区)

    • si: 每秒从磁盘交换进内存的内存量(swap in)
    • so: 每秒从内存交换到磁盘的内存量(swap out)
  • io (输入/输出)

    • bi: 每秒从块设备(如磁盘)读取的数据量
    • bo: 每秒向块设备(如磁盘)写入的数据量
  • system (系统)

    • in: 每秒的中断次数,包括时钟中断
    • cs: 每秒的上下文切换次数
  • cpu (处理器)

    • us: 用户态(user space)的CPU使用时间百分比
    • sy: 系统态(kernel space)的CPU使用时间百分比
    • id: CPU空闲时间百分比
    • wa: 等待I/O的CPU时间百分比
    • st: 虚拟机的CPU时间百分比(通常在虚拟化环境中看到)

分析进程上下文

stress 是一种用于生成计算机系统的负载的简易工具,可以生成 CPU,内存,磁盘等不同类型的负载,这里用于模拟多进程。

测试环境为 Ubuntu 22.04.3,13th Intel i7-13700KF 16核。

首先测试大于核数的进程数,使用stress --cpu 24生成负载

procs -----------memory----------   ---swap-- -----io---- -system-- ------cpu-----
 r  b swpd free    buff    cache    si   so    bi    bo   in   cs   us   sy id  wa st
29  0 1792 2825936 1219512 19963496 0    0     0     4    0    0    0    0  100 0  0
25  0 1792 2827972 1219512 19963496 0    0     0     100  8026 6308 100  0  0   0  0
24  0 1792 2820320 1219512 19963496 0    0     0     0    6844 3576 100  0  0   0  0
24  0 1792 2818556 1219512 19964264 0    0     0     0    6531 1645 100  0  0   0  0
24  0 1792 2817308 1219512 19964200 0    0     0     0    6401 1490 100  0  0   0  0
27  0 1792 2811288 1219512 19969296 0    0     0     0    6758 2553 100  0  0   0  0
27  0 1792 2811064 1219512 19969564 0    0     0     176  7413 5142 100  0  0   0  0
24  0 1792 2810308 1219512 19969576 0    0     0     0    6805 3572 100  0  0   0  0

与空闲状态相比,首先是 us 占比上升至100,表明 CPU 被大量用户级任务占用,同时中断和上下文切换也显著增加,初步分析是进程时间片耗尽导致时钟中断并进行上下文切换。

使用 pidstat 进一步分析,输出结果如下

UID       PID   cswch/s nvcswch/s  Command
0   1285221      0.00    170.00  stress
0   1285222      0.00     16.00  stress
0   1285223      0.00    910.00  stress
0   1285224      0.00     30.00  stress
0   1285225      0.00     13.00  stress
0   1285226      0.00    104.00  stress
0   1285227      0.00    117.00  stress
0   1285228      0.00    205.00  stress
0   1285229      0.00     12.00  stress
0   1285230      0.00     15.00  stress
0   1285231      0.00     31.00  stress
0   1285232      0.00     66.00  stress
0   1285233      0.00     29.00  stress
0   1285234      0.00     58.00  stress
0   1285235      0.00     17.00  stress
0   1285236      0.00     49.00  stress
0   1285237      0.00     90.00  stress
0   1285238      0.00      1.00  stress
0   1285239      0.00     27.00  stress
0   1285240      0.00      8.00  stress
0   1285241      0.00     19.00  stress
0   1285242      0.00     15.00  stress
0   1285243      0.00     21.00  stress
0   1285244      0.00      2.00  stress

可以发现 stress 进程每秒均发生1到910不等的非自愿上下文切换。

进一步使用小于硬件线程数的 stress --cpu 16 测试

procs -----------memory----------   ---swap-- -----io---- -system-- ------cpu-----
 r  b swpd free    buff    cache    si   so    bi   bo   in   cs us sy id wa st
16  0 1792 3388348 1220360 19523672 0    0     0     4    0    0  0  0 100  0  0
18  0 1792 3386372 1220360 19523672 0    0     0     0 6182 6361 67  0 33  0  0
16  0 1792 3400048 1220360 19523672 0    0     0   416 6222 4952 67  0 33  0  0
16  0 1792 3405616 1220360 19518212 0    0     0     0 5809 3554 67  0 33  0  0
16  0 1792 3406320 1220360 19517928 0    0     0     0 5565 3400 67  0 33  0  0
16  0 1792 3404624 1220360 19517928 0    0     0     8 5433 2477 67  0 33  0  0
16  0 1792 3407340 1220360 19518192 0    0     0     0 6403 7397 67  0 33  0  0
16  0 1792 3404348 1220360 19518340 0    0     0   276 5467 2384 67  0 33  0  0
UID PID          cswch/s nvcswch/s  Command
0   1298009      0.00      0.50  stress
0   1298010      0.00      0.25  stress
0   1298011      0.00      0.75  stress
0   1298012      0.00      1.99  stress
0   1298013      0.00      1.00  stress
0   1298014      0.00      2.74  stress
0   1298015      0.00      1.24  stress
0   1298016      0.00      0.25  stress
0   1298017      0.00      0.75  stress
0   1298018      0.00      2.99  stress
0   1298019      0.00      0.50  stress
0   1298020      0.00      0.25  stress
0   1298021      0.00      1.49  stress
0   1298022      0.00      2.49  stress
0   1298023      0.00      6.72  stress
0   1298024      0.00      1.00  stress

CPU 的占用率下降到 67%,同时非自愿上下文切换也大幅回落,证明在 16 进程的工作负载下 CPU 不是瓶颈。

分析协程上下文

编写一个死循环程序用于模拟计算密集型协程,与进程相同选择24和16作为并发协程数进行测试,测试结果如下:

24 协程:

procs -----------memory----------   ---swap-- -----io---- -system-- ------cpu-----
 r  b swpd free    buff    cache    si   so    bi   bo   in   cs us sy id wa st
24  0 1792 3794916 1220428 19425956 0    0     0     4    0    0  0  0 100  0  0
25  0 1792 3793408 1220428 19426308 0    0     0    96 7891 2697 100  0  0  0  0
24  0 1792 3789436 1220428 19430148 0    0     0     8 8030 3639 100  0  0  0  0
24  0 1792 3789084 1220428 19430152 0    0     0    12 8235 4478 100  0  0  0  0
24  0 1792 3792836 1220428 19430152 0    0     0     0 7769 1902 100  0  0  0  0
24  0 1792 3793128 1220428 19430152 0    0     0    48 7742 1932 100  0  0  0  0
24  0 1792 3792664 1220428 19429960 0    0     0     0 8009 3012 100  0  0  0  0
24  0 1792 3792664 1220428 19429924 0    0     0   296 7750 1971 100  0  0  0  0

16 协程:

procs -----------memory----------   ---swap-- -----io---- -system-- ------cpu-----
 r  b swpd free    buff    cache    si   so    bi   bo   in   cs us sy id wa st
16  0   1792 3717260 1220420 19518996    0    0     0     4    0    0  0  0 100  0  0
16  0   1792 3705428 1220420 19519044    0    0     0     0 6443 2831 67  0 33  0  0
16  0   1792 3701440 1220420 19521092    0    0     0    40 8615 8233 68  0 32  0  0
16  0   1792 3703652 1220420 19521092    0    0     0    44 6601 2933 67  0 33  0  0
16  0   1792 3713864 1220420 19521092    0    0     0    44 6559 3068 67  0 33  0  0
16  0   1792 3712604 1220420 19521092    0    0     0     0 6300 2314 67  0 33  0  0
16  0   1792 3710336 1220420 19521028    0    0     0    40 6448 2508 67  0 33  0  0
16  0   1792 3713736 1220420 19518468    0    0     0   136 6637 3801 67  0 33  0  0

可以看出虽然协程降低了上下文切换成本,但是并不是软件工程的银弹,在面对计算密集型任务时的表现与进程相同。