🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
## 2 平均负载 平均负载是指单位时间内,系统处于可运行状态和不可中断状态的平均进程数,也就是平均活跃进程数,它和CPU使用率并没有直接关系 > 获取平均负载 ```shell $ uptime 13:52:16 up 2 days, 16:20, 1 user, load average: 0.81, 0.75, 0.68 # 当前时间、系统运行时间以及正在登录用户数、过去1分钟、5分钟、15分钟的平均负载(Load Average) ``` 平均负载最理想的情况是等于**CPU个数** > 判断系统有几个CPU ```shell $ cat /proc/cpuinfo | grep 'model name' | wc -l 20 ``` CPU 使用率,是单位时间内 CPU 繁忙情况的统计,跟平均负载并不一定完全对应: - CPU 密集型进程,使用大量 CPU 会导致平均负载升高,此时这两者是一致的 - I/O 密集型进程,等待 I/O 也会导致平均负载升高,但 CPU 使用率不一定很高 - 大量等待 CPU 的进程调度也会导致平均负载升高,此时的CPU使用率也会比较高 通过 iostat、mpstat、pidstat 等工具,找出平均负载升高的根源 ----- **场景一:CPU 密集型进程** > 在第一个终端运行 stress 命令,模拟一个 CPU 使用率 100% 的场景 ```shell $ stress --cpu 1 --timeout 600 ``` > 接着,在第二个终端运行 uptime,查看平均负载的变化情况 ```shell # -d 参数表示高亮显示变化的区域 $ watch -d uptime ``` > 最后,在第三个终端运行mpstat查看 CPU 使用率的变化情况: ```shell # -P ALL 表示监控所有CPU,后面数字5表示间隔5秒后输出一组数据 $ mpstat -P ALL 5 ``` > 使用 pidstat 来查询哪个进程导致了 CPU 使用率升高 ```shell # 间隔5秒后输出一组数据 $ pidstat -u 5 1 ``` ----- **场景二:I/O 密集型进程** > 运行 stress 命令,模拟 I/O 压力,即不停地执行 sync ```shell $ stress -i 1 --timeout 600 ``` > 在第二个终端运行 uptime 查看平均负载的变化情况 ```shell $ watch -d uptime ``` > 第三个终端运行 mpstat 查看 CPU 使用率的变化情况 ```shell # 显示所有 CPU 的指标,并在间隔 5 秒输出一组数据 $ mpstat -P ALL 5 1 ``` ---- **场景三:大量进程的场景** > 使用 stress,但这次模拟的是 25 个进程 ```shell $ stress -c 25 --timeout 600 ``` ``` $ uptime ``` ``` $ pidstat -u 5 1 ``` 小结: - 平均负载高有可能是 CPU 密集型进程导致的 - 平均负载高并不一定代表 CPU 使用率高,还有可能是 I/O 更繁忙了 - 当发现负载高的时候,可以使用 mpstat、pidstat 等工具,辅助分析负载的来源 ## 3 CPU 上下文切换 根据任务的不同,CPU 的上下文切换可以分为几个不同的场景:进程上下文切换、线程上下文切换以及中断上下文切换 **进程上下文切换** 会重新加载 Task 的上下文到CPU寄存器和程序计数器 ![进程的运行空间分为内核空间和用户空间](https://static001.geekbang.org/resource/image/4d/a7/4d3f622f272c49132ecb9760310ce1a7.png "进程的运行空间分为内核空间和用户空间") - 内核空间(Ring 0)具有最高权限,可以直接访问所有资源 - 用户空间(Ring 3)只能访问受限资源,不能直接访问内存等硬件设备,必须通过**系统调用**(发生两次CPU上下文切换)陷入到内核中,才能访问这些特权资源 - 但是系统调用过程通常称为特权模式切换,而不是上下文切换。因为进程上下文切换,是指从一个进程切换到另一个进程运行,而系统调用过程中一直是同一个进程在运行 每次上下文切换都需要几十纳秒到数微秒的 CPU 时间([Tsuna 的测试报告](https://blog.tsunanet.net/2010/11/how-long-does-it-take-to-make-context.html)) 为了排查出现上下文切换的性能问题,需要了解**进程被调度到 CPU 上运行的时机**: - 某个进程的 CPU 时间片耗尽了,就会被系统挂起,切换到其它正在等待 CPU 的进程运行 - 进程在系统资源不足(比如内存不足)时,要等到资源满足后才可以运行,这个时候进程也会被挂起,并由系统调度其他进程运行 - 当进程通过睡眠函数 sleep 这样的方法将自己主动挂起时,自然也会重新调度 - 当有优先级更高的进程运行时,为了保证高优先级进程的运行,当前进程会被挂起,由高优先级进程来运行 - 发生硬件中断时,CPU上的进程会被中断挂起,转而执行内核中的中断服务程序 ---- **线程上下文切换** 会重新加载线程私有数据 线程与进程最大的区别在于,**线程是调度的基本单位,而进程则是资源拥有的基本单位**;多线程上下文切换比多进程上下文切换消耗更少的资源 ---- **中断上下文切换** 会打断进程的正常调度和执行,转而调度中断处理程序,响应设备事件 对于同一个CPU,中断处理比进程拥有更高的优先级,所以中断上下文切换与进程上下文切换不会同时发生 中断上下文切换也需要消耗CPU ---- 小结: - CPU 上下文切换,是保证 Linux 系统正常工作的核心功能之一,一般情况下不需要特别关注 - 但过多的上下文切换,会把CPU时间消耗在寄存器、内核栈以及虚拟内存等数据的保存和恢复上,从而缩短进程真正运行的时间,导致系统的整体性能大幅下降 ---- > 通过 vmstat 获取系统总体的上下文切换情况 ```shell $ vmstat procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu----- r b swpd free buff cache si so bi bo in cs us sy id wa st 1 0 797696 10637784 2301852 11319852 0 0 4 26 3 2 1 0 98 0 0 # cs(context switch) 每秒上下文切换次数 # in(interrupt) 每秒中断次数 # r(Running or Runable) 就绪队列的长度,即正在运行和等待CPU的进程数 # b(Blocked) 处于不可中断睡眠状态的进程数 ``` > 通过 pidstat 获取每个进程的详细情况 ```shell $ pidstat -w 5 Linux 5.10.18-amd64-desktop (swan) 2021年06月11日 _x86_64_ (20 CPU) 21时56分26秒 UID PID cswch/s nvcswch/s Command 21时56分31秒 0 1 1.59 0.60 systemd 21时56分31秒 0 12 4.37 0.00 ksoftirqd/0 21时56分31秒 0 13 63.02 0.00 rcu_sched # cswch(voluntary context switches),每秒自愿上下文切换次数:指进程无法获取所需资源,导致的上下文切换(例如 I/O、内存等系统资源不足时) # nvcswch(non voluntary context switches),每秒非自愿上下文切换次数:指进程由于时间片已到等原因,被系统强制调度,进而发生的上下文切换(例如大量进程都在争抢 CPU) ```