Linux性能优化
个人学习记录文档,欢迎补充
平时在实施、交付、运维时总是会遇到机子宕机的情况,遇到该紧急情况的时候,我们除了reboot、kill,还需要会精准定位系统问题,快速发现核心所在,所以阅读《Linux性能优化》并记录下相关笔记,在博客中也分享出来。
如果有误,请联系本人。
平均负载
平均负载是指单位时间内,处于可运行状态和不可中断状态的进程数。
所以,它不仅包括了正在使用 CPU 的进程,还包括等待 CPU 和等待 I/O的进程。
而 CPU 使用率,是单位时间内 CPU 繁忙情况的统计,跟平均负载并不一定完全对应。比如:
- CPU 密集型进程,使用大量 CPU 会导致平均负载升高,此时这两者是一致的;
- I/O 密集型进程,等待 I/O 也会导致平均负载升高,但 CPU 使用率不一定很高;
- 大量等待 CPU 的进程调度也会导致平均负载升高,此时的 CPU 使用率也会比较高。
平均负载提供了一个快速查看系统整体性能的手段,反映了整体的负载情况。但只看平均负载本身,我们并不能直接发现,到底是哪里出现了瓶颈。所以,在理解平均负载时,也要注意:
- 平均负载高有可能是 CPU 密集型进程导致的;
- 平均负载高并不一定代表 CPU 使用率高,还有可能是 I/O 更繁忙了;
- 当发现负载高的时候,你可以使用
mpstat
、pidstat
等工具,辅助分析负载的来源。
1 |
|
上下文切换
上下文切换是 CPU 从一个进程或线程切换到另一个进程或线程时所发生的操作,是多任务操作系统实现并发的基础,通常是由操作系统的调度程序(Scheduler)控制的,以确保多任务处理的效率。
过多的上下文切换,会把 CPU 时间消耗在寄存器、内核栈以及虚拟内存等数据的保存和恢复上,缩短进程真正运行的时间,成了系统性能大幅下降的一个元凶。
根据任务的不同,CPU 的上下文切换就可以分为几个不同的场景:
进程上下文切换
进程上下文切换指的是操作系统从一个进程切换到另一个进程时,需要保存和恢复进程的状态。一个进程的状态包括进程的寄存器、程序计数器、内存管理信息等。当发生进程切换时,操作系统需要将当前进程的所有状态保存到内存中,并将新进程的状态加载到 CPU 中。
- 开销:进程上下文切换开销较大,因为需要保存和恢复大量的数据,包括整个进程的虚拟内存、打开的文件描述符等。
- 触发条件:进程调度(例如时间片用完)或中断(如系统调用)。
线程上下文切换
线程上下文切换是指在同一个进程内部的多个线程之间切换。与进程上下文切换相比,线程上下文切换的开销较小,因为线程共享进程的地址空间和资源。
- 开销:线程切换比进程切换要小,因为线程共享大部分资源,仅需要保存和恢复少量的线程上下文,如寄存器和栈指针。
- 触发条件:线程调度(如多线程程序中的时间片用完)或系统调用。
中断上下文切换
中断上下文切换是指由于外部硬件中断(如 I/O 设备请求)或软件中断(如系统调用)引发的上下文切换。中断发生时,CPU 会中断当前进程的执行,转而执行中断处理程序。中断上下文切换需要保存当前执行进程的状态,并加载中断处理程序的上下文。
- 开销:中断上下文切换的开销较小,因为它通常只涉及少量的状态保存和恢复,但频繁的中断可能会影响系统性能。
- 触发条件:硬件中断或软件中断。
中断次数变多了,说明 CPU 被中断处理程序占用,需要通过查看
/proc/interrupts
文件来分析具体的中断类型。/proc
实际上是 Linux 的一个虚拟文件系统,用于内核空间与用户空间之间的通信。/proc/interrupts
就是这种通信机制的一部分,提供了一个只读的中断使用情况
上下文切换的类型:
**自愿上下文切换 cswch:是指进程无法获取所需资源,导致的上下文切换**。
比如说, I/O、内存等系统资源不足时,就会发生自愿上下文切换;
自愿上下文切换多了,说明进程都在等待资源,有可能发生了 I/O 等其他问题。
**非自愿上下文切换 nvcswch:是指进程由于时间片已到等原因,被系统强制调度,进而发生的上下文切换**。
比如说,大量进程都在争抢 CPU 时,就容易发生非自愿上下文切换;
非自愿上下文切换变多了,说明进程都在被强制调度,也就是都在争抢 CPU,说明 CPU 的确成了瓶颈。
不过不管是哪种场景导致的上下文切换,都应该知道:
- CPU 上下文切换,是保证 Linux 系统正常工作的核心功能之一,一般情况下不需要我们特别关注。
- 但过多的上下文切换,会把 CPU 时间消耗在寄存器、内核栈以及虚拟内存等数据的保存和恢复上,从而缩短进程真正运行的时间,导致系统的整体性能大幅下降。
1 |
|
CPU使用率
底层
Linux 作为一个多任务操作系统,将每个 CPU 的时间划分为很短的时间片,再通过调度器轮流分配给各个任务使用,因此造成多任务同时运行的错觉。
为了维护 CPU 时间,Linux 通过事先定义的节拍率(内核中表示为 HZ),触发时间中断,并使用全局变量 Jiffies 记录了开机以来的节拍数。每发生一次时间中断,Jiffies 的值就加 1。
节拍率 HZ 是内核的可配选项,可以设置为 100、250、1000 等。不同的系统可能设置不同数值,你可以通过查询 /boot/config 内核选项来查看它的配置值。
同时,正因为节拍率 HZ 是内核选项,所以用户空间程序并不能直接访问。为了方便用户空间程序,内核还提供了一个用户空间节拍率 USER_HZ,它总是固定为 100,也就是 1⁄100 秒。这样,用户空间程序并不需要关心内核中 HZ 被设置成了多少,因为它看到的总是固定值 USER_HZ。
Linux 通过/proc
虚拟文件系统,向用户空间提供了系统内部状态的信息,而/proc/stat
提供的就是系统的 CPU 和任务统计信息。
性能分析工具给出的都是间隔一段时间的平均 CPU 使用率,所以要注意间隔时间的设置
比如:
top 显示了系统总体的 CPU 和内存使用情况,以及各个进程的资源使用情况。
top
默认使用 3 秒时间间隔ps 则只显示了每个进程的资源使用情况。
ps
使用的是进程的整个生命周期
分析
perf 是 Linux 2.6.31 以后内置的性能分析工具。它以性能事件采样为基础,不仅可以分析系统的各种事件和内核性能,还可以用来分析指定应用程序的性能问题。
可以使用
perf top -g -p 123
查找指定进程中函数的递归调用情况。详见指令部分
CPU 使用率是最直观和最常用的系统性能指标,更是我们在排查性能问题时,通常会关注的第一个指标。所以我们更要熟悉它的含义,尤其要弄清楚用户(%user)、Nice(%nice)、系统(%system) 、等待 I/O(%iowait) 、中断(%irq)以及软中断(%softirq)这几种不同 CPU 的使用率。
比如说:
用户
CPU 和Nice
CPU 高,说明用户态进程占用了较多的 CPU,所以应该着重排查进程的性能问题。系统
CPU 高,说明内核态
占用了较多的 CPU,所以应该着重排查内核线程或者系统调用的性能问题。I/O 等待
CPU 高,说明等待 I/O 的时间比较长,所以应该着重排查系统存储是不是出现了 I/O 问题。软中断
和硬中断
高,说明软中断或硬中断的处理程序占用了较多的 CPU,所以应该着重排查内核中的中断服务程序。
碰到 CPU 使用率升高的问题,你可以借助 top、pidstat 等工具,确认引发 CPU 性能问题的来源;再使用 perf 等工具,排查出引起性能问题的具体函数。
但不是所有CPU使用率高的问题都可以这么分析,系统的 CPU 使用率不仅包括进程用户态和内核态的运行,还包括中断处理、等待 I/O 以及内核线程等。
相关指令
sysstat
sysstat
包含了常用的 Linux 性能工具,用来监控和分析系统的性能。
1 |
|
mpstat
mpstat
是一个常用的多核 CPU 性能分析工具,用来实时查看每个 CPU 的性能指标,以及所有 CPU 的平均指标。
1 |
|
输出数据样例
1 |
|
pidstat
pidstat
是一个常用的进程性能分析工具,用来实时查看进程的 CPU、内存、I/O 以及上下文切换等性能指标。
1 |
|
输出数据样例
1 |
|
perf
perf top
:查找热点函数
1 |
|
输出数据样例
1 |
|
第一行包含三个数据,分别是采样数(Samples)、事件类型(event)和事件总数量(Event count)。如采样数过少(比如只有十几个),下面的排序和百分比则没什么实际参考价值。
再往下是一个表格式样的数据,每一行包含四列,分别是:
- Overhead ,是该符号的性能事件在所有采样中的比例,用百分比来表示
- Shared ,是该函数或指令所在的动态共享对象(Dynamic Shared Object),如内核、进程名、动态链接库名、内核模块名等
- Object ,是动态共享对象的类型。比如
[.]
表示用户空间的可执行程序、或者动态链接库,而[k]
则表示内核空间- Symbol 是符号名,也就是函数名。当函数名未知时,用十六进制的地址来表示
可以输入参数:**
-p ...
指定pid来查看进程内的调用关系,最终定位到进程内资源占用最大的函数**
perf record
:提供了保存数据的功能
perf report
:解析展示保存后的数据
1 |
|
在实际使用中,我们还经常为
perf top
和perf record
加上-g
参数,开启调用关系的采样,方便根据调用链来分析性能问题。
CPU
查看系统总体的 CPU 和内存使用情况,以及各个进程的资源使用情况:
1 |
|
输出数据样例
1 |
|
- PID: 进程的唯一标识符(Process ID)。每个运行中的进程都有一个唯一的PID。
- USER: 启动该进程的用户。如果进程是以特定用户身份运行的,这里会显示该用户的用户名。
- PR: 进程的优先级(Priority)。数值越小,优先级越高。进程的优先级可以影响其CPU的分配。
- NI: 进程的“nice”值(Nice Value)。这个值影响进程的优先级,范围通常是-20(最高优先级)到19(最低优先级)。负值表示更高的优先级,正值表示更低的优先级。
- VIRT: 进程使用的虚拟内存总量,包括进程的所有代码和数据,以及可能未被实际使用的内存。
- RES: 进程使用的常驻内存(Resident Memory),即实际在RAM中占用的内存量,不包括交换区(swap)。
- SHR: 进程共享的内存量(Shared Memory),即与其他进程共享的内存量。
- S: 进程的状态(Status)。常见的状态有:
- R: 运行中(Running)
- S: 睡眠中(Sleeping)
- Z: 僵尸进程(Zombie)
- T: 停止(Stopped)
- %CPU: 进程使用的CPU占用率,表示该进程在总CPU时间中的占比。
- %MEM: 进程使用的物理内存占用率,表示该进程占用的内存与系统总内存的百分比。
- **TIME+**: 进程使用的总CPU时间,格式为hh:mm:ss,表示该进程在所有CPU上运行的总时间。
- COMMAND: 启动该进程的命令名称或路径。
us用户、sy系统、ni-Nice、id空闲时间、wa等待IO的CPU时间、hi处理硬中断CPU时间、si处理软中断CPU时间、st系统运行在虚拟机中时其他虚拟机占用CPU时间
查看CPU核心数量:
1 |
|
查看系统的 CPU 和任务统计信息:
1 |
|
输出数据样例
1 |
|
- user(通常缩写为 us),代表用户态 CPU 时间。注意,它不包括下面的 nice 时间,但包括了 guest 时间。
- nice(通常缩写为 ni),代表低优先级用户态 CPU 时间,也就是进程的 nice 值被调整为 1-19 之间时的 CPU 时间。这里注意,nice 可取值范围是 -20 到 19,数值越大,优先级反而越低。
- system(通常缩写为 sys),代表内核态 CPU 时间。
- idle(通常缩写为 id),代表空闲时间。注意,它不包括等待 I/O 的时间(iowait)。
- iowait(通常缩写为 wa),代表等待 I/O 的 CPU 时间。
- irq(通常缩写为 hi),代表处理硬中断的 CPU 时间。
- softirq(通常缩写为 si),代表处理软中断的 CPU 时间。
- steal(通常缩写为 st),代表当系统运行在虚拟机中的时候,被其他虚拟机占用的 CPU 时间。
- guest(通常缩写为 guest),代表通过虚拟化运行其他操作系统的时间,也就是运行虚拟机的 CPU 时间。
- guest_nice(通常缩写为 gnice),代表以低优先级运行虚拟机的时间。
上下文
uptime
:查看系统平均负载
1 |
|
vmstat
:分析系统的内存使用情况,也常用来分析 CPU 上下文切换和中断的次数
1 |
|
- cs(context switch)是每秒上下文切换的次数。
- in(interrupt)则是每秒中断的次数。
- r(Running or Runnable)是就绪队列的长度,也就是正在运行和等待 CPU 的进程数。
- b(Blocked)则是处于不可中断睡眠状态的进程数。
GPU
nvidia-smi
:查看英伟达GPU占用