前言 作为一名测试人员,难免需要维护业务线的测试环境。初时每当研发通知某某机器又挂了、某某机器太卡了,笔者完全两眼一抹黑,无从入手。而当了解了linux基本性能,熟悉其基本指标时,遇到问题往往心里有底,甚至得心应手。送人玫瑰,手有余香,特此整理,与君共享。性能指标CPU使用率 提起性能指标,最容易想到的是CPU使用率。linux作为一个多任务系统,将每个CPU的时间划分为很短的时间片,通过调度器轮流分配给各个任务使用,造成多任务同时运行的错觉。而根据CPU上执行任务的不同,又可以细分为用户CPU、系统CPU、等待IOCPU、软中断CPU和硬中断CPU等。用户CPU使用率(usni),包括用户态CPU使用率(user)和低优先级用户态CPU使用率(nice),表示CPU在用户状态运行的时间百分比。用户CPU使用率高,说明有应用程序比较繁忙。系统CPU使用率(sy),表示CPU在内核状态运行的时间百分比。系统CPU使用率高,说明内核比较繁忙。等待IO使用率(wa),也称为iowait,表示等待IO的时间百分比。iowait高,说明系统与硬件设备的IO交互时间比较长。软中断(si)和硬中断(hi)的CPU使用率,分别表示内核调用软中断处理程序、硬中断处理程序的时间百分比。它们的使用率高,通常说明系统发生了大量的中断。CPU使用率如何计算 CPU使用率描述了非空闲时间占总CPU时间的百分比。用公式表示为: 可以通过以下命令查看各个CPU的数据:catprocstatgrepcpucpuusnisysidwahisistguestgnicecpu4284969901820500143969582531068983544211571447151747397324892000cpu014488978309918260556249620723421175894371162080282872000cpu118028499788539182700339444025341223815215138950000cpu218467545199857177641895405553744179900504945465000cpu3185474119210151175660372399834166174861304771745000。。。 这里输出的是一个表格,第一列为CPU的编号,如cpu0,而第一行没有CPU编号,表示所有CPU的累加。其他列表示不同任务下CPU的时间,每一列的顺序可以通过manproc命令查看。通过这些数据就可以计算出当前机器的CPU使用率。然而,得到的CPU使用率是开机以来的平均CPU使用率,一般不具有参考价值。性能工具一般会取间隔一段时间的两次差值,计算这段时间内的平均CPU使用率,即: 如何查看CPU使用率 top和ps是常用的性能分析工具。top能够显示系统总体的CPU和内存使用情况,以及各进程的资源使用情况。ps显示各进程的资源使用情况。例如,top的输出格式为:toptop21:23:36up283days,10:41,2users,loadaverage:21。96,14。46,13。76Tasks:2009total,10running,1994sleeping,0stopped,5zombieCpu(s):32。5us,7。6sy,0。0ni,58。3id,0。6wa,0。0hi,0。9si,0。0stMem:98979248ktotal,95372168kused,3607080kfree,1479704kbuffersSwap:0ktotal,0kused,0kfree,59440700kcachedPIDUSERPRNIVIRTRESSHRSCPUMEMTIMECOMMAND13159user2009103m6。0g13mS1686。4156503:16mongod31963root200929m882m672R520。90:01。61supervisord。。。 其中,第三行CPU就是该机器的CPU使用率,top默认每3秒刷新一次。默认情况下显示的是所有CPU的平均值,如果想了解每个CPU的使用情况,只需按下数字1即可。例如:Cpu0:17。0us,10。2sy,0。0ni,64。9id,2。3wa,0。0hi,5。6si,0。0stCpu1:12。1us,5。5sy,0。0ni,82。1id,0。0wa,0。0hi,0。3si,0。0stCpu2:24。5us,4。0sy,0。0ni,71。2id,0。0wa,0。0hi,0。3si,0。0st。。。 从上我们可以发现,top并没有细分进程的用户态CPU和内核态CPU,如果想了解每个进程的详细情况,可以使用pidstat工具。例如:pidstat11Linux3。2。023generic(cs)06242021x8664(24CPU)09:32:41PMPIDusrsystemguestCPUCPUCommand09:32:42PM15220。880。880。001。7720beam。smp。。。分析 一般来说,每种类型的CPU使用率高,都有不同导致的条件,下面列出了这几种导致不同CPU使用率高的情况以及着重排查点:用户CPU和NiceCPU高,说明用户态进程占用了较多的CPU,所以应该着重排查进程的性能问题。系统CPU高,说明内核态占用了较多的CPU,所以应该着重排查内核线程或者系统调用的性能问题。IO等待CPU高,说明等待IO的时间比较长,所以应该着重排查系统存储是不是出现了IO问题。软中断和硬中断高,说明软中断或硬中断的处理程序占用了较多的CPU,所以应该着重排查内核中的中断服务程序。平均负载 第二能想到的就是平均负载。很多人会认为,平均负载不就是单位时间内的CPU使用率吗?其实并非如此。下面我们来一起熟悉下什么是平均负载。 通俗地说,平均负载是指单位时间内系统处于可运行状态和不可中断状态的平均进程数,即平均活跃进程数。从定义我们可以解读到其实跟CPU使用率无直接关系。linux将进程的状态划分为以下几种: R是Running的缩写,表示进程正在运行或者正在等待运行。 D是DiskSleep的缩写,为不可中断睡眠状态,进程正与硬件进行交互,交互过程不允许被其他进程打断,以防止数据丢失。 Z是Zombie的缩写,表示僵尸进程,进程实际已经结束,但是父进程还没回收其资源,就会出现该状态。 S是InterruptibleSleep的缩写,称为可中断睡眠状态,表示进程因等待某个事件而被系统挂起。 I是Idle的缩写,为空闲状态,用于不可中断睡眠的内核线程上。 T是Traced的缩写,表示进程处于暂停状态。 X是Dead的缩写,表示进程已经消亡。如何查看 通常使用uptime或top命令查看。例如,uptime的输出格式为:uptime15:22:50up284days,4:40,3users,loadaverage:10。06,12。15,12。33 输出分别表示为当前时间、系统运行时间、正在登录用户数以及过去1分钟,5分钟,15分钟的平均负载。输出这三个时间的平均负载有什么含义呢?数值基本相同,说明系统负载趋于平稳。数值从过去15分钟到过去1分钟逐渐升高,说明系统运行任务在增多,需要我们去观察和判断这种升高是否合理。数值从过去15分钟到过去1分钟在逐渐降低,说明系统运行任务在减少。 基于此就可以粗略判断当前的系统负载情况。上面的例子可以看到目前的系统负载下降的。多少为合理 平均负载这一数据没有针对CPU个数作归一化处理,因此最理想情况是平均负载等于CPU的个数。在评判当前系统是否过载时,首先需要知道当前系统的CPU个数,可以通过top或者lscpu查看。例如:lscpuArchitecture:x8664CPUopmode(s):32bit,64bitByteOrder:LittleEndianCPU(s):24。。。 当知道了CPU个数后,就可以结合平均负载来判断当前系统的运行情况了。一旦负载过高,会导致进程响应变慢,影响服务的正常功能。故当出现负载有明显升高趋势时,就需要我们去分析和调查了。通常认为平均负载低于CPU数量70时为合理状态,但不是绝对的,还需要参考负载的变化趋势来作判断。CPU上下文切换 前面描述了linux系统会在很短的时间内把CPU轮流分配给任务(进程),造成多个任务同时运行的错觉。而这些任务都是从哪里加载以及开始运行的呢?这就引出了CPU上下文。CPU寄存器和程序计数器被叫做CPU的上下文,那么,CPU寄存器和程序计数器分别有什么作用呢?CPU寄存器,存放数据的小型存储区域,用来暂时存放参与运算的数据和运算结果。程序计数器,用来存储CPU下一条指令位置。 而CPU上下文切换,就是把前一个任务的CPU上下文保存起来,然后加载新任务的上下文到寄存器和程序计数器中,最后跳转到程序计数器所指位置,执行新任务。根据任务的不同,可以分为三种切换场景:进程上下文切换、线程上下文切换以及中断上下文切换。进程上下文切换 提到进程上下文切换,就不得不提到系统调用。linux按照特权等级,把进程的运行空间划分为内核空间和用户空间。内核空间,具有最高权限,可以访问所有资源。用户空间,访问受限,不能直接访问内存等硬件设备,只能通过系统调用到内核中,由内核访问。 系统调用和普通函数调用非常相似,只不过,系统调用由操作系统核心提供,运行于内核态,而普通函数调用由函数库或用户提供,运行于用户态。比如,某一进程需要查看文件内容时,就需要系统调用来完成:首先调用open()打开文件,通过read()读取文件内容,并通过write()写到到标准输出,最后通过close()关闭文件。系统调用结束后,CPU寄存器恢复到原来保存的用户态,切换到用户空间,继续执行进程。 从上我们可以知道,系统调用是在进程内进行的,其与进程上下文切换有点不同,它不会涉及到虚拟内存等用户态资源。所以我们通常称系统调用为一种特权模式切换,而在系统调用用上下文切换是不可避免的,一次系统调用发生了两次上下文切换。 进程是由内核管理和调度的,进程的上下文切换在内核态进行,故其上下文切换要比系统调用多了一步:在保存当前进程的内核状态和CPU寄存器之前,需要先把该进程的虚拟内存、栈等用户资源保存起来。 那么,什么时候需要进行上下文切换呢?进程的系统资源不足时,进程就会被挂起,等到资源充足时才能进行。进程通过sleep睡眠函数等方法主动挂起时。当有更高优先级的进程运行时,例如中断等,进程会被挂起,来保证高优先级进程正常运行。线程上下文切换 我们知道,线程是调度的基本单位,而进程是资源拥有的基本单位,即内核中的任务调度实际对象是线程,进程只是给线程提供了虚拟内存等资源。如果进程中有多个线程,则线程会共享虚拟内存等资源。因此,对于线程上下文切换就有两种情况:前后切换线程属于同一进程,则虚拟内存等资源就不需要切换,只需要切换线程需要的私有资源。前后切换线程属于两个进程,此时跟进程上下文切换一致。 由此我们知道同一进程的线程上下文切换,要比进程间的上下文切换耗费更少资源,这也是多线程代替多进程的一大优势。中断上下文切换 中断要比进程的优先级更高,其会打断进程的正常调度,因此中断程序需要更短小精悍,以便尽可能快地结束。 中断上下文切换与进程不同,其不需要虚拟内存、全局变量等用户态资源,只需要如CPU寄存器、硬件中断参数等内核态中断服务程序所需要的状态。如何查看 查看系统总体的上下文切换情况可以通过vmstat命令,其输出格式为:vmstat11procsmemoryswapiosystemcpurbswpdfreebuffcachesisobiboincsussyidwa1000165388814778526163030400011200738190 其中,需要关注以下几个参数:r(running),正在运行和等待运行的进程数。b(blocked),不可中断睡眠状态的进程数。in(interrupt),每秒中断次数。cs(contextswitch),每秒的上下文切换次数。 而如果我们想要查看进程的上下文切换,可以通过pidstat查看,输出格式为:通过参数w可以查看进程每秒的上下文切换次数pidstatw11Linux3。2。023generic(cs)06262021x8664(24CPU)03:19:25PMPIDcswchsnvcswchsCommand03:19:26PM10。850。00init03:19:26PM30。850。00ksoftirqd0。。。 其中,需要关注两个参数:cswch(voluntarycontextswitch),每秒自愿上下文切换次数。当进程无法获取资源,比如内存、IO等资源不足时,就会发生自愿上下文切换。nvcswch(nonvoluntarycontextswitch),每秒非自愿上下文切换次数。当进程的时间片已到,比如大量进程争抢CPU时,就会被系统强制调度,发生非自愿上下文切换。 如果我们想要查看线程间的上下文切换,还是可以通过pidstat查看,加上wt参数选项,输出格式为:pidstatwt11linux3。2。023generic(cs7)06262021x8664(24CPU)03:34:07PMTGIDTIDcswchsnvcswchsCommand03:34:07PM22000。640。00flush8:4803:34:07PM22000。640。00flush8:4803:34:07PM2524136。540。00influxd03:34:07PM253726。920。00influxd03:34:07PM370112。823。211scheduler03:34:07PM37020。640。002scheduler03:34:07PM37251。280。00aux。。。多少为合理 据统计,每次上下文切换需要耗费几十纳秒到数微秒的CPU时间,这个时间还是可以接受的。但是过多的上下文切换,会把CPU时间都耗费在寄存器、内存栈以及虚拟内存的保存和恢复上,缩短进程真正运行的时间,导致系统性能大幅下降。那么,我们肯定想了解,多少的CPU上下文切换为合理情况呢? 这取决于CPU的性能。如果上下文切换稳定,那么从数百到上万都是合理的。但是当CPU上下文切换呈数量级增大时,或者数值已超过上万次且不断增长时,就可能出现了性能问题,需要我们着重排查。CPU缓存命中率 CPU缓存是为了协调CPU处理速度和内存访问速度之间的差距而出现的空间,可分为L1、L2和L3三级缓存。离CPU核心越近,缓存的读写速度就越快,因此三级缓存的读写速度快慢分别是:L1L2L3。 如果CPU所操作的数据在缓存中,则可以直接读取,称为缓存命中。命中缓存可以带来很大的性能提升。因此,考虑CPU的缓存命中率对于linux性能也至关重要。 查看系统的内存情况,可通过top或free命令。比如,free命令输出格式为:freetotalusedfreesharedbufferscachedMem:989792489756225214169960147800062018692Swap:000 输出分别是物理内存Mem和交换分区Swap的使用情况。其中,每一列分别是:total,总内存大小。used,已使用内存的大小。free,未使用的内存大小。shared,共享内存的大小。bufferscached,缓存和缓冲区的大小 查看buffers和cache的变化情况,可通过vmstat命令,输出格式:vmstat11procsmemoryswapiosystemcpurbswpdfreebuffcachesisobiboincsussyidwa900157164014780446203479200011200738190buffer和cache就是上面所说的缓存和缓冲区的大小,单位为KB。bi和bo分别表示块设备读取和写入的大小,单位为块秒。linux中块的大小是1KB,故单位等价于KBs。实践 下面就目前机器出现的系统使用率过高的情况进行排查。 通过top可以查看当前的系统性能情况:toptop16:41:29up285days,5:59,2users,loadaverage:9。82,11。53,12。92Tasks:2010total,9running,2000sleeping,0stopped,1zombieCpu(s):34。5us,6。5sy,0。0ni,57。6id,0。5wa,0。0hi,0。9si,0。0stMem:98979248ktotal,97548032kused,1431216kfree,1478084kbuffersSwap:0ktotal,0kused,0kfree,62122812kcachedPIDUSERPRNIVIRTRESSHRSCPUMEMTIMECOMMAND13159user2009128m6。1g13mS906。5160516:12mongod19638root200929m882m520R580。90:01。81supervisord。。。 CPU用户使用率为34。5,系统使用率为6。5,空闲时间占57。6,系统使用流畅。可以看出,当前的系统是比较空闲的。 然后,我执行某些操作。再通过top命令查看当前的系统性能情况:topTasks:2020total,16running,2004sleeping,0stopped,0zombieCpu(s):64。5us,13。6sy,0。0ni,20。5id,0。2wa,0。0hi,1。1si,0。0stMem:98979248ktotal,98003524kused,975724kfree,1478128kbuffersSwap:0ktotal,0kused,0kfree,62228740kcachedPIDUSERPRNIVIRTRESSHRSCPUMEMTIMECOMMAND13159user2009128m6。2g13mS2496。5160538:08mongod15108root200929m882m620R730。90:02。24supervisord15121root200929m882m620R660。90:02。03supervisord15122root200929m882m620R660。90:02。03supervisord。。。 可以发现,系统使用率已经增至了原来的一倍。CPU使用率除了服务mongod最高以外,有几个supervisord启动的服务也出现了CPU使用率增高的情况。通过pidstat命令分析这些进程的系统资源占用情况:pidstatp15108Linux3。2。023generic(cs)06262021x8664(24CPU)05:03:14PMPIDusrsystemguestCPUCPUCommand 发现该进程并没有输出任何的资源使用信息。是pidstat指令出故障了?可以通过ps指令来交叉确认一下:psauxgrep151082003142110。00。08748948pts2S17:070:00grepcolorauto15108 确实没有输出。现在发现问题了,原来是进程已经不存在了,所以pidstat没有任何输出。继续分析其他几个高CPU使用率的进程,发现亦如此。既然进程没有了,那么性能问题应该已经没有了吧。通过top命令确认下:topTasks:2019total,18running,2001sleeping,0stopped,0zombieCpu(s):60。5us,13。4sy,0。0ni,24。7id,0。4wa,0。0hi,1。1si,0。0stMem:98979248ktotal,98118260kused,860988kfree,1478272kbuffersSwap:0ktotal,0kused,0kfree,62353676kcachedPIDUSERPRNIVIRTRESSHRSCPUMEMTIMECOMMAND13159user2009129m6。2g13mS2606。5160559:54mongod27114root200929m882m580R630。90:01。94supervisord27145root200929m882m600R600。90:01。87supervisord。。。 好像问题依旧存在,系统CPU使用率依旧很高。仔细查看top的输出,发现supervisord进程的pid每次都有变化,现在已经变成了27114。进程的PID号在变化,说明了什么?有两点:进程在不断的重启,比如因为段错误、配置错误等,进程退出后可能又被监控系统重启了。这些进程都是短时进程。这些进程一般只运行很短时间结束,很难通过top发现。 supervisor是一个进程管理程序,能够监控进程状态,异常退出时能自动重启。目前看来,极有可能是第一种情况。通过supervisor来查看服务状态: 从uptime更新时间长短可以发现,很多服务在不断地重启。可能是服务配置或者代码问题引起的。其实是批量的服务出现配置问题导致了服务启动失败,然后supervisor监控到服务没有成功启动后又自动重启。在解决完这些服务问题后,通过top再次看看性能情况:topTasks:2012total,7running,2002sleeping,0stopped,3zombieCpu(s):29。7us,5。4sy,0。0ni,63。5id,0。8wa,0。0hi,0。5si,0。0stMem:98979248ktotal,96231348kused,2747900kfree,1470892kbuffersSwap:0ktotal,0kused,0kfree,60741708kcachedPIDUSERPRNIVIRTRESSHRSCPUMEMTIMECOMMAND13159user2009131m6。2g13mS806。6160593:01mongod27545user2004188066362896S610。00:01。86python27549root200929m882m616R380。90:01。15supervisord。。。总结 在动手进行性能优化前,需要考虑以下几个问题:如何判断优化是有效的呢?有多个性能问题同时发生时,应该先优化哪一个?提升方法有多种时,应该选择哪一种? 对于第一个问题,我们解决性能问题的目的,自然有一个性能提升的效果,即要对性能指标进行量化,需要确认优化前指标、优化后指标。当优化后指标与优化前指标有不同时,才能判定我们的优化有没有效果。比如上述例子中的系统CPU使用率优化前后的变化。 对于第二个问题,有一个二八原则,也就是80的问题都是由20的代码导致的。因此,并不是所有的性能问题都值得优化。在动手优化前,往往需要把所有性能问题分析一遍,找出最重要的、可以最大程度提升性能的问题。这个过程会花费较多的时间,下面有两个可以简化这一过程的方法:如果发现出现系统瓶颈问题,那么首先优化的一定是系统资源问题。针对不同类型的指标,首先要去优化那些性能指标变化幅度最大的问题。 对于第三个问题,一般来说,应该选择能最大程度提升性能的方法。但是需要注意的是,性能优化并非没有成本,很有可能你优化了一个指标,另一个指标的性能就变差了。参考https:time。geekbang。orgcolumnarticle69618https:time。geekbang。orgcolumnarticle69859https:time。geekbang。orgcolumnarticle70476https:time。geekbang。orgcolumnarticle72147http:www。lyyyuna。com20200529perftestanalysiscpu1https:juejin。cnpost6844903608371118094https:linuxtoolsrst。readthedocs。iozhCNlatesttooltop。html