对于一台linux服务器来讲,总共有三种资源 CPU、磁盘、内存。三种资源消耗到红线的状态也不同
内存检查
如果是内存将要耗尽,服务器的响应速度会变慢,当前连接会话输入命令时会有相依延迟的现象,启动一个新的程序会出现 os:memory 报错,此时执行free命令查看
bash
[root@node4 ~]# free -h
total used free shared buff/cache available
Mem: 1.9G 108M 1.7G 8.6M 143M 1.7G
Swap: 2.0G 0B 2.0G
看最后一列 available 表示还有多少内存可以用来分配,如果它没多少或者随着查询快速下降,此时就表示存在程序吃内存
此时,使用 vmstat 查询系统资源监控,每隔 1 秒输出一次
bash
[root@node4 ~]# vmstat 1
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 0 1773332 2108 145276 0 0 31 4 31 35 0 0 100 0 0
0 0 0 1773332 2108 145276 0 0 0 0 52 47 0 0 100 0 0
0 0 0 1773332 2108 145276 0 0 0 0 36 33 0 0 99 0 0
0 0 0 1773332 2108 145276 0 0 0 0 49 43 0 0 100 0 0
监控中,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内存倒排
bash
top - 17:30:44 up 1:20, 1 user, load average: 0.00, 0.01, 0.01
Tasks: 116 total, 2 running, 114 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.0 us, 0.0 sy, 0.0 ni,100.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 2031912 total, 1765472 free, 114056 used, 152384 buff/cache
KiB Swap: 2097148 total, 2097148 free, 0 used. 1743048 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
923 root 20 0 562404 16560 5876 S 0.0 0.8 0:00.51 tuned
956 root 20 0 158860 12016 712 S 0.0 0.6 0:00.00 kadmind
685 polkitd 20 0 534132 10976 4600 S 0.0 0.5 0:00.01 polkitd
683 root 20 0 471824 8852 6580 S 0.0 0.4 0:00.11 NetworkMa+
1 root 20 0 128220 6860 4076 S 0.0 0.3 0:00.61 systemd
691 root 20 0 305092 6128 4764 S 0.0 0.3 0:03.42 vmtoolsd
684 root 20 0 99620 6076 4468 S 0.0 0.3 0:00.01 VGAuthSer+
688 root 20 0 214248 5580 3208 S 0.0 0.3 0:00.25 rsyslogd
1034 root 20 0 145716 5200 3924 S 0.0 0.3 0:00.15 sshd
514 root 20 0 46952 5036 2836 S 0.0 0.2 0:00.04 systemd-u+
513 root 20 0 190792 4140 2572 S 0.0 0.2 0:00.00 lvmetad
922 root 20 0 106012 4076 3096 S 0.0 0.2 0:00.00 sshd
1015 postfix 20 0 89728 4016 3012 S 0.0 0.2 0:00.00 qmgr
1014 postfix 20 0 89660 3992 2988 S 0.0 0.2 0:00.03 pickup
929 root 20 0 154584 3616 824 S 0.0 0.2 0:00.00 krb5kdc
491 root 20 0 36828 2760 2444 S 0.0 0.1 0:00.04 systemd-j+
1126 root 20 0 157688 2132 1496 R 0.0 0.1 0:00.04 top
PID 是进程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 ,你要记录下目标进程的 PID,top命令展示的是个十进制数,你需要更具不同类型的程序做处理,比如目标是个Java程序,就需要 printf 命令转换成十六进制
bash
# 如果这个Java程序用的是多线程,你要额外查询子进程的PID,内存排序的操作是一样的
top -Hp Java程序的十进制PID
#随后将目标PID转十六进制
printf "%x\n" 目标PID
随后使用 jstack 命令导出目标进程ID的系统日志快照。对于Java程序来讲在文件中搜索十六进制的进程ID,就能看到堆栈信息,就和Java报错格式一样能看到是那个类哪行代码触发的堆栈信息,以及进程运行的状态
bash
jstack 目标十进制PID > 储存到文件
如果不是个Java,就不需要这么麻烦。
对于 C 原生程序,你需要安装一个工具
bash
yum install valgrind
# 随后启动一个的目标 c 程序的伴生进程
valgrind --tool=massif 目标程序路径
在运行一段实际后,结束 c 程序,在运行valgrind的路径下会生成一个 massif.out.<PID> 的文件,使用分析命令,即可查看堆内存使用报告
ms_print massif.out.<PID>
如果是个 python程序,需要安装工具
bash
pip install memray
# 随后和 c 程序一样,附件到目标进程一个伴生程序。运行足够时间后,按 Ctrl+C 停止采集
memray attach --aggregate -o 结果输出到的路径 进程PID
# 结果数据有两种使用方式
memray table 结果输出到的路径 --sort 文本路径 # 以文本图标的方式,看哪个函数累计分配最多
memray flamegraph 结果输出到的路径 -o flame.html # 生成火焰图,浏览器看
如果你想一招鲜,linux有个通用工具,但是注意这个工具需要4.1以上的内核
bash
yum install bcc-tools
/usr/share/bcc/tools/memleak -p 11111 -t -a 5
# -p 指定进程,-t 显示调用栈,-a 5 输出前 5 个
# 持续观察一段时间,它会定期报告哪些分配点累积了最多内存却没有释放
Attaching to pid 11111, Ctrl+C to end.
[21:03:12] Top 5 stacks with outstanding allocations:
8 outstanding allocations of total 50331648 bytes:
-> b'allocate_token_bucket' + 0x3f [rust_service; addr=0x4221a0]
-> b'handle_connection' + 0x1a2 [rust_service; addr=0x4055e2]
...
输出直接指出函数名 allocate_token_bucket 和 handle_connection。
对于目标进程是原生二进制文件,可以直接用命令 `addr2line -e 进程的二进制执行文件 0x4221a0 `就能定位到具体源文件中的行号
addr2line 命令是专门用来查询二进制地址在一个二进制可执行文件中的位置的
CPU检查
如果你查看 top 命令的监控时,发现内存相关的检查点没有特殊的异常但是服务很卡,就去看 cpu 运行百分比,键入大写 P 就可以按照 %CPU 排序
bash
Tasks: 114 total, 1 running, 113 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.0 us, 0.0 sy, 0.0 ni,100.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 2031912 total, 1525240 free, 116544 used, 390128 buff/cache
KiB Swap: 2097148 total, 2097148 free, 0 used. 1715032 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1148 root 20 0 0 0 0 S 0.3 0.0 0:01.08 kworker/2+
1 root 20 0 128220 6860 4076 S 0.0 0.3 0:00.67 systemd
2 root 20 0 0 0 0 S 0.0 0.0 0:00.02 kthreadd
3 root 20 0 0 0 0 S 0.0 0.0 0:00.04 ksoftirqd+
5 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 kworker/0+
6 root 20 0 0 0 0 S 0.0 0.0 0:00.26 kworker/u+
7 root rt 0 0 0 0 S 0.0 0.0 0:00.00 migration+
8 root 20 0 0 0 0 S 0.0 0.0 0:00.00 rcu_bh
9 root 20 0 0 0 0 S 0.0 0.0 0:00.97 rcu_sched
10 root rt 0 0 0 0 S 0.0 0.0 0:00.05 watchdog/0
11 root rt 0 0 0 0 S 0.0 0.0 0:00.03 watchdog/1
12 root rt 0 0 0 0 S 0.0 0.0 0:00.00 migration+
13 root 20 0 0 0 0 S 0.0 0.0 0:00.01 ksoftirqd+
15 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 kworker/1+
16 root rt 0 0 0 0 S 0.0 0.0 0:00.04 watchdog/2
17 root rt 0 0 0 0 S 0.0 0.0 0:00.00 migration+
18 root 20 0 0 0 0 S 0.0 0.0 0:00.00 ksoftirqd+
随后的排查思路和上面的内存排查差不多,比如 Java 从程序都是导出堆栈日志,查看当前执行的是什么代码,就能知道哪里在吃CPU
C 原生程序,用perf工具
bash
yum install -y perf
# 录制 30 秒,-g 记录调用栈 ,-p 进程号
perf record -p 3456 -g -- sleep 30
# 30秒后,使用report查看记录到等信息
perf report
Samples: 120K of event 'cycles', Event count (approx.): 48000000000
Children Self Symbol
+ 99.00% 0.00% [.] main
+ 98.50% 0.00% [.] process_request
+ 97.20% 90.30% [.] hash_loop <--- 这里占用了 90% CPU
perf工具还可以实时看看cpu热点,相当于看cpu的一招鲜办法
bash
# 直接执行看的是所有,可以带上 -p 看某个进程的
[root@node4 opt]# perf top
Samples: 56 of event 'cpu-clock', 4000 Hz, Event count (approx.): 7871188 lost:
Overhead Shared Object Symbol
49.58% [kernel] [k] _raw_spin_unlock_irqrestore
11.40% [kernel] [k] generic_exec_single
6.12% [kernel] [k] mpt_put_msg_frame
3.76% [kernel] [k] e1000_xmit_frame
2.85% [kernel] [k] kallsyms_expand_symbol.constprop.1
2.78% [kernel] [k] rcu_idle_exit
2.13% [kernel] [k] run_timer_softirq
1.43% [kernel] [k] __memcpy
1.43% [kernel] [k] __x2apic_send_IPI_mask
1.43% [kernel] [k] finish_task_switch
1.43% [kernel] [k] format_decode
1.43% [kernel] [k] module_get_kallsym
1.43% [kernel] [k] number.isra.2
1.43% [kernel] [k] queue_delayed_work_on
1.43% [kernel] [k] smp_call_function_many
1.43% [kernel] [k] strlcpy
1.43% [kernel] [k] vsnprintf
1.43% libc-2.17.so [.] _IO_getdelim
1.43% libc-2.17.so [.] __libc_calloc
1.43% perf [.] __dso__load_kallsyms
1.43% perf [.] hex2u64
Overhead 该函数消耗的 CPU 时间占比,所有行加起来接近 100%。Shared Object 函数所在的二进制文件或库,[kernel] = Linux 内核,libc-2.17.so = C 标准库,perf = perf 工具自身代码,普通程序名(如 java, nginx)会直接显示。Symbol 具体的函数名。[k] 表示内核空间函数,[.] 表示用户空间函数。
磁盘检查
同理,如果你发现 top 命令中磁盘写入读出的数据块很高,也可以定位程序。不过磁盘通常不会检查怎么细,因为磁盘的影响范围比较局限,它不会把整个服务器拖垮,最多只会使得某一个路径在其他程序读取它时陷入长久的IO等待,严重的时候读取的程序会报一个内核GC错误
但如果你确实需要看是那个具体的代码在操作数据块,linux也能看,用的也是 perf
bash
# 录制 10 秒,关注对应进程所有"通用块层 I/O 请求发出"的内核事件
perf record -e block:block_rq_issue -p 7766 -g -- sleep 10
# 查看报告,包含调用栈
perf report --stdio
[root@node4 opt]# perf record -e block:block_rq_issue -p 1148 -g -- sleep 10
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.011 MB perf.data (5 samples) ]
[root@node4 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,0 R 8 (4a 01 00 00 10 00 00 00 08 00) 0 + 0 [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