如何检查服务器上消耗资源的程序是那个
对于一台linux服务器来讲总共有三种资源 CPU、磁盘、内存。三种资源消耗到红线的状态也不同内存检查如果是内存将要耗尽服务器的响应速度会变慢当前连接会话输入命令时会有相依延迟的现象启动一个新的程序会出现 os:memory 报错此时执行free命令查看[rootnode4 ~]# free -htotal usedfreeshared buff/cache available Mem:1.9G 108M1.7G8.6M 143M1.7G Swap:2.0G 0B2.0G看最后一列 available 表示还有多少内存可以用来分配如果它没多少或者随着查询快速下降此时就表示存在程序吃内存此时使用 vmstat 查询系统资源监控每隔 1 秒输出一次[rootnode4 ~]# vmstat 1procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu----- r b swpdfreebuff cache si so bi boincs us syidwa st10017733322108145276003143135001000000017733322108145276000052470010000000177333221081452760000363300990000017733322108145276000049430010000监控中r 表示有多少个进程在等待核时b 表示有多少进程在阻塞。memory 下的四个指标中free 和 直接用 free -h 命令看到的结果基本是一样的这里的单位是 kb但往往它会存在很小的可能因为linux对内存的使用遵循能用就用的原则将空闲内存大量用作各种缓存所以会存在服务器上没运行很多繁杂任务但是这里的剩余内存就是很小。当确实存在吃内存的程序时free 会明显下降但是 buff cache也会下降也就是说 buff cache 它并不包含进程消耗内存数量它仅仅只是内核对磁盘读取的缓存当其他进程大量消耗内存时会挤压它们两个使用额度。swap 下的 si 和 so 代表交换内存的换入和换出在内存紧张且没有关闭交换分区能力时会频繁变动且 swpd 值也会较大它表示当前有多少数据存放在用来做交换内存的磁盘以及文件中。bi 和 bo 表示当前系统每秒共向磁盘读取和写入多少数据块它包含了交换分区的数量。system 下的 in 记录当前有多少个需要cpu注意的中断信号这里说的中断是值进程完成、文件写入完成、进程由于错误而中断等等所有需要cpu决策的情况一般不重要cs 是内核上下文切换次数指标也就是在不同进程之间切换in 会影响 cs 的数量但不是一比一的数量关系。cpu 下的指标中us 表示 cpu 在用户态进程普通应用程序、计算任务的时间百分比sy 表示 cpu 在处理内核态处理系统调用、管理内存、处理中断、I/O 等的时间百分比id 表示 cpu 处于完全空闲时间的百分比wa 表示 cpu 因为 io 未完成而陷入等待的时间百分比st 表示如果当前服务器是个虚拟机的话cpu 被宿主机挪用的时间百分比言归正传当内存进入红线交换分区的指标会频繁变动缓存指标会减少free 可用内存也会很小。此时需要使用 top 命令查询内存消耗情况进入后按英文大写 M 会进入%MEM内存倒排top-17:30:44 up1:20,1user, load average:0.00,0.01,0.01Tasks:116total,2running,114sleeping,0stopped,0zombie %Cpu(s):0.0us,0.0sy,0.0ni,100.0 id,0.0wa,0.0hi,0.0si,0.0st KiB Mem:2031912total,1765472free,114056used,152384buff/cache KiB Swap:2097148total,2097148free,0used.1743048avail Mem PIDUSERPR NI VIRT RES SHR S %CPU %MEM TIME COMMAND923root200562404165605876S0.00.80:00.51 tuned956root20015886012016712S0.00.60:00.00 kadmind685polkitd200534132109764600S0.00.50:00.01 polkitd683root20047182488526580S0.00.40:00.11 NetworkMa1root20012822068604076S0.00.30:00.61 systemd691root20030509261284764S0.00.30:03.42 vmtoolsd684root2009962060764468S0.00.30:00.01 VGAuthSer688root20021424855803208S0.00.30:00.25 rsyslogd1034root20014571652003924S0.00.30:00.15 sshd514root2004695250362836S0.00.20:00.04 systemd-u513root20019079241402572S0.00.20:00.00 lvmetad922root20010601240763096S0.00.20:00.00 sshd1015postfix2008972840163012S0.00.20:00.00 qmgr1014postfix2008966039922988S0.00.20:00.03 pickup929root2001545843616824S0.00.20:00.00 krb5kdc491root2003682827602444S0.00.10:00.04 systemd-j1126root20015768821321496R0.00.10:00.04topPID 是进程ID。USER 是启动进程的用户。PR 是内核视角的瞬时任务优先级数字越小优先级越高。NI 是任务本身的优先级范围 -20 到 19 同样数字越小优先级越高它会影响 PR。VIRT 使用虚拟内存总量 。RES 使用物理内存总量。SHR 和其他进程共享的内存总量。S 是进程状态R 表示运行S 表示可自主的中断D 表示不可自主的中断(也就是当前进程在等一个非自己可控的事情的结果比如磁盘不响应等等这些它自己不是自己想中断的状态)Z 表示以结束但资源还没来的急回收T 表示被暂停。%CPU 表示上次筛选当现在占用 cpu 的时间。%MEM 表示进程占物理内存的百分比。TIME 表示进程自启动以来累计使用的 CPU 时间格式 分:秒.百分秒精度比 TIME 高。COMMAND 表示进程的命令行名称或启动命令如果你想看完整命令键入小写 c排查内存问题主要看 RES 你要记录下目标进程的 PIDtop命令展示的是个十进制数你需要更具不同类型的程序做处理比如目标是个Java程序就需要 printf 命令转换成十六进制# 如果这个Java程序用的是多线程你要额外查询子进程的PID内存排序的操作是一样的top-HpJava程序的十进制PID#随后将目标PID转十六进制printf%x\n目标PID随后使用 jstack 命令导出目标进程ID的系统日志快照。对于Java程序来讲在文件中搜索十六进制的进程ID就能看到堆栈信息就和Java报错格式一样能看到是那个类哪行代码触发的堆栈信息以及进程运行的状态jstack 目标十进制PID储存到文件如果不是个Java就不需要这么麻烦。对于 C 原生程序你需要安装一个工具yuminstallvalgrind# 随后启动一个的目标 c 程序的伴生进程valgrind--toolmassif 目标程序路径 在运行一段实际后结束 c 程序在运行valgrind的路径下会生成一个 massif.out.PID的文件使用分析命令即可查看堆内存使用报告 ms_print massif.out.PID如果是个 python程序需要安装工具pipinstallmemray# 随后和 c 程序一样附件到目标进程一个伴生程序。运行足够时间后按 CtrlC 停止采集memray attach--aggregate-o结果输出到的路径 进程PID# 结果数据有两种使用方式memray table 结果输出到的路径--sort文本路径# 以文本图标的方式看哪个函数累计分配最多memray flamegraph 结果输出到的路径-oflame.html# 生成火焰图浏览器看如果你想一招鲜linux有个通用工具但是注意这个工具需要4.1以上的内核yuminstallbcc-tools /usr/share/bcc/tools/memleak-p11111-t-a5# -p 指定进程-t 显示调用栈-a 5 输出前 5 个# 持续观察一段时间它会定期报告哪些分配点累积了最多内存却没有释放Attaching to pid11111, CtrlC to end.[21:03:12]Top5stacks with outstanding allocations:8outstanding allocations of total50331648bytes: -ballocate_token_bucket 0x3f[rust_service;addr0x4221a0]-bhandle_connection 0x1a2[rust_service;addr0x4055e2]... 输出直接指出函数名 allocate_token_bucket 和 handle_connection。 对于目标进程是原生二进制文件可以直接用命令addr2line-e进程的二进制执行文件 0x4221a0就能定位到具体源文件中的行号 addr2line 命令是专门用来查询二进制地址在一个二进制可执行文件中的位置的CPU检查如果你查看 top 命令的监控时发现内存相关的检查点没有特殊的异常但是服务很卡就去看 cpu 运行百分比键入大写 P 就可以按照 %CPU 排序Tasks:114total,1running,113sleeping,0stopped,0zombie %Cpu(s):0.0us,0.0sy,0.0ni,100.0 id,0.0wa,0.0hi,0.0si,0.0st KiB Mem:2031912total,1525240free,116544used,390128buff/cache KiB Swap:2097148total,2097148free,0used.1715032avail Mem PIDUSERPR NI VIRT RES SHR S %CPU %MEM TIME COMMAND1148root200000S0.30.00:01.08 kworker/21root20012822068604076S0.00.30:00.67 systemd2root200000S0.00.00:00.02 kthreadd3root200000S0.00.00:00.04 ksoftirqd5root0-20000S0.00.00:00.00 kworker/06root200000S0.00.00:00.26 kworker/u7root rt0000S0.00.00:00.00 migration8root200000S0.00.00:00.00 rcu_bh9root200000S0.00.00:00.97 rcu_sched10root rt0000S0.00.00:00.05 watchdog/011root rt0000S0.00.00:00.03 watchdog/112root rt0000S0.00.00:00.00 migration13root200000S0.00.00:00.01 ksoftirqd15root0-20000S0.00.00:00.00 kworker/116root rt0000S0.00.00:00.04 watchdog/217root rt0000S0.00.00:00.00 migration18root200000S0.00.00:00.00 ksoftirqd随后的排查思路和上面的内存排查差不多比如 Java 从程序都是导出堆栈日志查看当前执行的是什么代码就能知道哪里在吃CPUC 原生程序用perf工具yuminstall-yperf# 录制 30 秒-g 记录调用栈 -p 进程号perf record-p3456-g--sleep30# 30秒后使用report查看记录到等信息perf report Samples: 120K of eventcycles, Event count(approx.):48000000000Children Self Symbol 99.00%0.00%[.]main 98.50%0.00%[.]process_request 97.20%90.30%[.]hash_loop--- 这里占用了90% CPUperf工具还可以实时看看cpu热点相当于看cpu的一招鲜办法# 直接执行看的是所有可以带上 -p 看某个进程的[rootnode4 opt]# perf topSamples:56of eventcpu-clock,4000Hz, Event count(approx.):7871188lost: Overhead Shared Object Symbol49.58%[kernel][k]_raw_spin_unlock_irqrestore11.40%[kernel][k]generic_exec_single6.12%[kernel][k]mpt_put_msg_frame3.76%[kernel][k]e1000_xmit_frame2.85%[kernel][k]kallsyms_expand_symbol.constprop.12.78%[kernel][k]rcu_idle_exit2.13%[kernel][k]run_timer_softirq1.43%[kernel][k]__memcpy1.43%[kernel][k]__x2apic_send_IPI_mask1.43%[kernel][k]finish_task_switch1.43%[kernel][k]format_decode1.43%[kernel][k]module_get_kallsym1.43%[kernel][k]number.isra.21.43%[kernel][k]queue_delayed_work_on1.43%[kernel][k]smp_call_function_many1.43%[kernel][k]strlcpy1.43%[kernel][k]vsnprintf1.43% libc-2.17.so[.]_IO_getdelim1.43% libc-2.17.so[.]__libc_calloc1.43% perf[.]__dso__load_kallsyms1.43% perf[.]hex2u64Overhead 该函数消耗的 CPU 时间占比所有行加起来接近 100%。Shared Object 函数所在的二进制文件或库[kernel] Linux 内核libc-2.17.so C 标准库perf perf 工具自身代码普通程序名如 java, nginx会直接显示。Symbol 具体的函数名。[k] 表示内核空间函数[.] 表示用户空间函数。磁盘检查同理如果你发现 top 命令中磁盘写入读出的数据块很高也可以定位程序。不过磁盘通常不会检查怎么细因为磁盘的影响范围比较局限它不会把整个服务器拖垮最多只会使得某一个路径在其他程序读取它时陷入长久的IO等待严重的时候读取的程序会报一个内核GC错误但如果你确实需要看是那个具体的代码在操作数据块linux也能看用的也是 perf# 录制 10 秒关注对应进程所有“通用块层 I/O 请求发出”的内核事件perf record-eblock:block_rq_issue-p7766-g--sleep10# 查看报告包含调用栈perf report--stdio[rootnode4 opt]# perf record -e block:block_rq_issue -p 1148 -g -- sleep 10[perf record: Woken up1timestowritedata][perf record: Captured and wrote0.011MB perf.data(5samples)][rootnode4 opt]# perf report --stdio# To display the perf.data header info, please use --header/--header-only option### Total Lost Samples: 0## Samples: 5 of event block:block_rq_issue# Event count (approx.): 5## Children Self Trace output# ........ ........ ..........................................................#100.00%100.00%0,0R8(4a 01 00 001000 00 00 08 00)00[kworker/2:0|---ret_from_fork kthread worker_thread process_one_work disk_events_workfn disk_check_events sr_block_check_events cdrom_check_events sr_check_events scsi_execute_req_flags scsi_execute blk_execute_rq blk_execute_rq_nowait __blk_run_queue scsi_request_fn blk_peek_request## (Tip: Profiling branch (mis)predictions with: perf record -b / perf report)#磁盘影响最恶劣的也就至于一个磁盘自身坏了这种你写一个文件进去写入失败与否就知道磁盘坏没坏了有时候对于服务器的检查不是上面这种出了问题才做后置检查。而是判断这台服务器能不能满足应用的需求比如说现在有台服务器让你判断磁盘的 QPS 能不能达到预期要求这个时候 top命令就不行了就需要使用另外的命令工具了可以看-》https://blog.csdn.net/dudadudadd/article/details/144566868