平均负载

top/uptime显示的平均负载到底是啥意思?它的值多少是比较合理的?

Linux系统中,不知道的命令、库和系统调用等都可以执行下man命令来查看。

man uptime说明:

​ System load averages is the average number of processes that are either in a runnable or uninterruptable state. A process in a runnable state is either using the CPU or waiting to use the CPU. A process in uninterruptable state is waiting for some I/O access, eg waiting for disk. The averages are taken over the three time intervals. Load averages are not normalized for the number of CPUs in a system, so a load average of 1 means a single CPU system is loaded all the time while on a 4 CPU system it means it was idle 75% of the time.

也可以从man在线文档查询用户指令详细内容

翻译过来就是说,系统平均负载是可运行或者不可中断状态的进程在一个时间间隔内的平均值。可运行是指进程正在占用CPU或者等待使用CPU(ps命令中的R状态),不可中断就是等待IO响应(ps命令中的D状态),如等待硬件设备磁盘的IO响应。平均负载为1意味着在单CPU系统中CPU时间被完全占用,而在4核CPU的话就有75%的空闲时间片。

这么说平均负载包含了两者,即:

  • 正在占用或等待CPU的任务(多任务竞争得不到CPU资源而等待)
  • 等待IO的任务

也可以理解为,在Linux系统中平均负载反映的是系统中所有非idle的进程

除了正在占用CPU外还有等待CPU和IO的任务,因此,CPU使用率和系统负载不是成比例关系的,在不同的场景下不同,比如IO密集或者CPU密集型服务分别对应的CPU等待IO和占用CPU的时间上可能较多。 CPU使用率还包含了多个方面,这些都是影响CPU和负载的重要因素,从top命令看有:

  • us:用户态
  • sy:内核态
  • si:软中断
  • hi:硬中断
  • ni:优先级
  • wa:IO wait
  • st:虚拟化中其他虚拟机窃取占用
  • guest:虚拟化中客户机占用

一般来说,平均负载不要高于cpu数量的70%(当然这个不是绝对值),即单核CPU的话负载就是不高于0.7。通过1分钟、5分钟、15分钟这三个值反应出短时间内负载的趋势走向。

综上,影响负载的因素可能是:

  • IO忙,此时可能会有较多D状态的进程,同时iowait占比较高
  • CPU密集进程,一般用户态us占比较高
  • 多任务竞争CPU导致的频繁上下文切换,通常内核态sy占比较高,上下文切换是数量级上的差别
  • 过多的中断,一般可以通过中断类型的变化情况watch -d "cat /proc/interrupts"watch -d "cat /proc/softirqs"来判断

上下文切换

linux是一个多任务操作系统,操作系统通过时间分片的方式给多任务分配CPU,所以大多的任务不是并行运行的。CPU在任务之间切换交替执行,这种机制就是上下文切换。操作系统保持跟踪任务运行所需的所有状态信息就是上下文,比如PC指针、寄存器的当前值和主存的内容等

举例,《深入理解计算机系统第三版》中在shell中执行hello程序为例:

image-20220112175720605

1
2
3
4
5
6
#include <stdio.h>
int main()
{
    printf("hello, world\n");
    return 0;
}

最开始只有shell进程在运行,当我们输入./hello后,shell通过系统调用来执行我们的请求,系统调用将控制权交给操作系统,操作系统保存shell进程的上下文,创建一个新的hello进程及其上下文,然后将控制权传给新的hello进程。hello进程终止后,操作系统恢复shell进程的上下文,并将控制权传回给它。因此用户态到内核态的特权模式切换也会发生上下文切换,且进程的切换只能发生在内核态

注意,保存和恢复上下文也需要在CPU上运行。Linux 通过 TLB(Translation Lookaside Buffer)来管理虚拟内存到物理内存的映射关系,当虚拟内存更新后,TLB 也需要刷新,内存的访问也会随之变慢。因此频繁的上下文切换会把CPU时间消耗在寄存器、内核栈、虚拟内存等等数据的保存和恢复上,它也是一个导致负载高的一个重要原因

何时发生进程/线程上下文切换?

  • 进程终止

  • 系统调用(一般称为特权模式切换)

  • 进程时间片用完了,系统会挂起此进程,切换到其他进程

  • 进程主动挂起,比如sleep

  • 进程等待所需资源,资源条件满足才可运行

  • 高优先级任务被调度

  • 中断上下文切换,同一个CPU,中断的优先级比进程的高,发生中断事件后保存当前进程上下文并执行中断处理程序

根据任务类型的不同,可分为三种上下文切换:

  • 进程上下文切换
  • 线程上下文切换
  • 中断上下文切换:如硬件触发的中断,系统执行中断处理程序

由主动还是被动切换又可分为两种:

  • 自愿上下文切换:进程无法获取所需资源,导致的上下文切换。比如说 I/O、内存等系统资源不足时,等待资源,切到其他任务执行,就会发生自愿上下文切换
  • 非自愿上下文切换:进程由于时间片已到等原因,被系统强制调度,进而发生的上下文切换。比如说,大量进程都在争抢 CPU 时,就容易发生非自愿上下文切换

每秒上下文切换多少次比较合理?这个也没有固定值,与CPU性能有很大关系,与CPU核数也有一定关系。我们在编码时也不宜创建过多线程导致资源竞争带来的过高开销。

Linux系统性能优化分析思路及工具可以参考这位大佬博客