引言
计算机科学中上下文指执行某个操作所需的相关状态。操作系统中的上下文切换是一种用于支持多任务处理的基本功能,允许单个处理器在多个进程或线程之间高效地切换,从而使计算机可以同时运行多个程序。上下文切换是必需的和有成本的,因此如何合理减少上下文切换和降低上下文切换的成本,是需要操作系统和开发者共同面对的问题。本文使用了vmstat工具,分别分析了进程、线程和协程上下文切换的成本。
上下文切换概述
进程上下文
进程是操作系统分配资源和调度的基本单位,拥有独立的地址空间和系统资源。一个进程的上下文包括:
- 寄存器状态:包括程序计数器、堆栈指针、通用寄存器等。
- 内存管理信息:如虚拟地址空间、页表等。
- I/O状态信息:包括文件描述符、网络连接状态等。
- 进程控制块 (PCB) :包含进程ID、进程状态(就绪、等待、运行)、调度优先级等。
线程上下文
线程是进程中的执行单元,线程间共享进程的地址空间和系统资源,但拥有自己的执行栈和寄存器集合。线程的上下文通常包括:
- 寄存器状态:每个线程都有自己的寄存器集,包括程序计数器和堆栈指针。
- 线程栈:每个线程都有自己的栈空间,用于存储执行过程中的局部变量、函数参数和返回地址。
- 线程控制块 (TCB) :包含线程ID、线程状态、调度信息等。
协程上下文
协程是用户态的轻量级“线程”,由程序自行管理,不由操作系统内核调度。协程相比线程更加轻量,切换开销较小。协程的上下文包括:
- 寄存器状态:程序计数器、通用寄存器等。协程通常使用的是线程的栈。
- 协程栈:协程可能有自己的栈,尽管这个栈通常比线程栈要小。
- 当前协程状态:记录了协程的当前状态,如运行、挂起等。
- 调度器指针:指向管理这个协程的调度器或协程管理器。
使用多种工具分析上下文切换
vmstat
(Virtual Memory Statistic)是一个监测虚拟内存、进程、CPU活动等系统性能的工具,在这里用来分析 CPU 上下文切换,在空闲状态下的使用示例如下
css
复制代码
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
生成负载
yaml
复制代码
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
进一步分析,输出结果如下
bash
复制代码
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
测试
yaml
复制代码
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
bash
复制代码
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 协程:
yaml
复制代码
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 协程:
yaml
复制代码
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
可以看出虽然协程降低了上下文切换成本,但是并不是软件工程的银弹,在面对计算密集型任务时的表现与进程相同。
原文链接:https://juejin.cn/post/7379058697618948096