总结几种linux下观察程序使用资源情况的工具,包括:CPU,内存,fd有无泄漏,IO有无异常(比如日志异常输出),网络IO有无异常。通过这几种工具监控程序运行时资源有无异常,让程序更加稳定。
CPU使用率
最常用的命令是top,它可以显示整个系统中所有进程的CPU使用情况并且可以进行排序,当然它不止可以监控CPU资源,还可以监控内存,IO等。默认以CPU的使用率排序。最简单的方式就是直接输入top命令查看整个系统资源使用情况。
通过top命令也可以监控进程中线程CPU的使用情况,默认也是按CPU使用情况排序
-H 是一个很有用的参数,可以直接查看进程中线程资源使用的情况,并且可以看到进程中线程数
top -H -p 18047
如上命令通过top查看进程18047所有线程的资源使用状态,如下是结果
top - 14:56:53 up 28 days, 4:12, 3 users, load average: 0.00, 0.03, 0.05
Threads: 15 total, 0 running, 15 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.9 us, 0.3 sy, 0.0 ni, 98.7 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 7747244 total, 1567056 free, 2786128 used, 3394060 buff/cache
KiB Swap: 7995388 total, 7646984 free, 348404 used. 4241380 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
18047 root 20 0 366744 21036 2464 S 0.0 0.3 0:07.73 RelayServer
18051 root 20 0 366744 21036 2464 S 0.0 0.3 0:03.08 RelayServer
18052 root 20 0 366744 21036 2464 S 0.0 0.3 0:00.00 RelayServer
18053 root 20 0 366744 21036 2464 S 0.0 0.3 0:17.26 RelayServer
18054 root 20 0 366744 21036 2464 S 0.0 0.3 0:26.31 RelayServer
18055 root 20 0 366744 21036 2464 S 0.0 0.3 0:09.40 RelayServer
18056 root 20 0 366744 21036 2464 S 0.0 0.3 0:11.53 RelayServer
18057 root 20 0 366744 21036 2464 S 0.0 0.3 0:04.05 RelayServer
18058 root 20 0 366744 21036 2464 S 0.0 0.3 0:24.14 RelayServer
18059 root 20 0 366744 21036 2464 S 0.0 0.3 0:17.90 RelayServer
18060 root 20 0 366744 21036 2464 S 0.0 0.3 0:38.72 RelayServer
18061 root 20 0 366744 21036 2464 S 0.0 0.3 0:22.37 RelayServer
18062 root 20 0 366744 21036 2464 S 0.0 0.3 0:42.98 RelayServer
18063 root 20 0 366744 21036 2464 S 0.0 0.3 0:14.47 RelayServer
18064 root 20 0 366744 21036 2464 S 0.0 0.3 1:56.67 RelayServer
软中断
linux中的软中断包括网络收发,定时,RCU锁等各种
,每个CPU都对应一个软中断内核线程,名字是ksoftirqd/CPU编号。当软中断事件的频率过高时,内核线程也会因为CPU使用率过高而导致软中断处理不及时,进而引发网络收发,调度缓慢等性能问题。
可以通过 watch -d cat /proc/softirqs 命令查看系统软中断次数,其中watch命令是变化,cat /proc/softirqs才是显示系统中真正的累计中断次数
一种分析网络服务的收发包是否正常的方式:通过查看软中断中NET_TX 和 NET_RX的增速是否很快,如果很快,CPU在处理大量网络中断,使用率肯定会增高。这个时候就需要查看程序收发包的代码逻辑,为什么会有大量的收发包情况。
如下命令输出,显示每个CPU上软中断的累计次数
Every 2.0s: cat /proc/softirqs Tue Jul 2 17:36:50 2019
CPU0 CPU1 CPU2 CPU3 CPU4 CPU5 CPU6 CPU7
HI: 2 0 0 0 0 0 2 0
TIMER: 3010721 2977489 3016155 3099307 2897057 3492281 2896383 2853132
NET_TX: 6201 43 69 201 914 1233 336 82
NET_RX: 897297 336229 907668 16233407 8454552 506387 877685 16687073
BLOCK: 4227 416 158020 147 158 65584 104 1391370
BLOCK_IOPOLL: 0 0 0 0 0 0 0 0
TASKLET: 3797 169 207 157 7311978 2827 668 15754714
SCHED: 1307215 474968 706726 330146 550114 258509 535334 326909
HRTIMER: 0 0 0 0 0 0 0 0
RCU: 1864254 1629223 1880544 1730405 1864998 1876487 1865292 1806708
在网络程序的协议设计中应尽量避免设计成小包协议(避免收发大量小包),这种情况会造成产生大量的的收发包中断,而影响性能
句柄泄漏
句柄泄漏是服务端编程经常遇到问题,这种问题的表现比较隐晦,它并不影响业务,但是如果服务长时间运行或在请求高峰值的情况下,句柄泄漏加剧,达到进程限制值。这个时候服务就无法处理新的请求了。所以在服务测试阶段就应该测试服务有无句柄泄漏的情况。
lsof -p 进程id
lsof列出了进行所有的资源,常见的包括文件fd,socket fd,eventfd,timerfd,eventpoll等,通过lsof命令可以查看指定进程打开的fd,根据linux下一切皆文件的思想
watch lsof -p 进程id
可以查看指定进程的fd的变化情况,判断出fd是否有泄漏。
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
RelayServ 18047 root cwd DIR 253,0 4096 3070583 /usr/local/smartseesip/siprelay
RelayServ 18047 root rtd DIR 253,0 4096 192 /
RelayServ 18047 root txt REG 253,0 7921129 3070563 /usr/local/smartseesip/siprelay/RelayServer
RelayServ 18047 root mem REG 253,0 2107816 201329626 /usr/lib64/libc-2.17.so
RelayServ 18047 root mem REG 253,0 88720 201326793 /usr/lib64/libgcc_s-4.8.5-20150702.so.1
RelayServ 18047 root mem REG 253,0 1141552 201329666 /usr/lib64/libm-2.17.so
RelayServ 18047 root mem REG 253,0 995840 201329703 /usr/lib64/libstdc++.so.6.0.19
RelayServ 18047 root mem REG 253,0 110808 201329686 /usr/lib64/libresolv-2.17.so
RelayServ 18047 root mem REG 253,0 19512 201329664 /usr/lib64/libdl-2.17.so
RelayServ 18047 root mem REG 253,0 142296 201329684 /usr/lib64/libpthread-2.17.so
RelayServ 18047 root mem REG 253,0 44088 201329688 /usr/lib64/librt-2.17.so
RelayServ 18047 root mem REG 253,0 164432 201329619 /usr/lib64/ld-2.17.so
RelayServ 18047 root 0r CHR 1,3 0t0 1028 /dev/null
RelayServ 18047 root 1w CHR 1,3 0t0 1028 /dev/null
RelayServ 18047 root 2w CHR 1,3 0t0 1028 /dev/null
RelayServ 18047 root 3w REG 253,0 0 137585576 /usr/local/smartseesip/siprelay/relay/relay.20190702-000220.18047.log
RelayServ 18047 root 4r FIFO 0,8 0t0 12209677 pipe
RelayServ 18047 root 5w FIFO 0,8 0t0 12209677 pipe
RelayServ 18047 root 6r FIFO 0,8 0t0 12209678 pipe
RelayServ 18047 root 7w FIFO 0,8 0t0 12209678 pipe
RelayServ 18047 root 8u IPv4 12209679 0t0 UDP 192.168.100.104:sentinel-lm
RelayServ 18047 root 9u a_inode 0,9 0 6765 [eventfd]
RelayServ 18047 root 10u a_inode 0,9 0 6765 [eventpoll]
RelayServ 18047 root 11u a_inode 0,9 0 6765 [timerfd]
RelayServ 18047 root 12u a_inode 0,9 0 6765 [eventfd]
RelayServ 18047 root 13u a_inode 0,9 0 6765 [eventpoll]
RelayServ 18047 root 14u a_inode 0,9 0 6765 [eventfd]
RelayServ 18047 root 15u a_inode 0,9 0 6765 [eventpoll]
RelayServ 18047 root 16u a_inode 0,9 0 6765 [timerfd]
RelayServ 18047 root 17u a_inode 0,9 0 6765 [timerfd]
RelayServ 18047 root 19u a_inode 0,9 0 6765 [eventfd]
RelayServ 18047 root 20u a_inode 0,9 0 6765 [eventpoll]
RelayServ 18047 root 21u a_inode 0,9 0 6765 [timerfd]
RelayServ 18047 root 23u a_inode 0,9 0 6765 [eventfd]
RelayServ 18047 root 24u a_inode 0,9 0 6765 [eventfd]
RelayServ 18047 root 25u a_inode 0,9 0 6765 [eventpoll]
RelayServ 18047 root 26u a_inode 0,9 0 6765 [timerfd]
RelayServ 18047 root 27u a_inode 0,9 0 6765 [eventpoll]
RelayServ 18047 root 28u a_inode 0,9 0 6765 [timerfd]
RelayServ 18047 root 31u a_inode 0,9 0 6765 [eventfd]
RelayServ 18047 root 32u a_inode 0,9 0 6765 [eventpoll]
RelayServ 18047 root 33u a_inode 0,9 0 6765 [timerfd]
RelayServ 18047 root 36u a_inode 0,9 0 6765 [eventfd]
RelayServ 18047 root 37u a_inode 0,9 0 6765 [eventpoll]
RelayServ 18047 root 38u a_inode 0,9 0 6765 [timerfd]
RelayServ 18047 root 41u a_inode 0,9 0 6765 [eventfd]
RelayServ 18047 root 42u a_inode 0,9 0 6765 [eventpoll]
RelayServ 18047 root 43u a_inode 0,9 0 6765 [timerfd]
RelayServ 18047 root 46u a_inode 0,9 0 6765 [eventfd]
RelayServ 18047 root 47u a_inode 0,9 0 6765 [eventpoll]
RelayServ 18047 root 48u a_inode 0,9 0 6765 [timerfd]
如上所示,lsof列出了RelayServer所打开的所有句柄,包括socket fd,文件,event fd,timer fd等
IO
服务的业务模型通常分为CPU密集型和IO密集型,CPU密集型就是服务需要占用CPU进行大量计算,比如视频转码服务,会议服务等。IO密集型是主要业务操作是IO操作,比如ftp服务,代理服务等。CPU密集型的服务CPU占用比较高,IO密集型服务CPU占用比较低(当然高低是个相对概念)。
top 命令的输出中有个wa占用比例可以用来判断当前系统的IO情况,如果服务是CPU密集型,这个wa占用率高,那么就应该要注意了,比如需要关注是否是在狂打日志。
通过 pidstat -d 来查看每个进程的IO情况,确定具体进程的IO情况,如果所关注的进程出现异常值(值比预期的大),那么就需要检查进程的代码逻辑了
[root@localhost GWStreamServer]# pidstat -d 5
Linux 3.10.0-327.el7.x86_64 (localhost.localdomain) 2019年07月02日 _x86_64_ (4 CPU)
21时52分11秒 UID PID kB_rd/s kB_wr/s kB_ccwr/s Command
21时52分16秒 0 1321 0.00 4.79 0.00 dmserver
21时52分16秒 UID PID kB_rd/s kB_wr/s kB_ccwr/s Command
21时52分21秒 0 1321 0.00 4.00 0.00 dmserver
21时52分21秒 0 2879 0.00 0.80 0.00 slapd
21时52分21秒 0 2881 0.00 2.40 0.00 java
21时52分21秒 0 3009 0.00 5.60 0.00 java
21时52分21秒 0 3036 0.00 2.40 0.00 java
网络
最常用的命令就是netstat,可以查看程序占用的端口及端口被哪个程序占用,可以查看tcp的状态,可以统计当前系统的网络协议信息
- 查看RelayServer的占用的端口
netstat -anp | grep RelayServer
输出如下,RelayServer占用udp端口 5093
udp 0 0 192.168.20.87:5093 0.0.0.0:* 2841/./RelayServer
- 查看端口554被哪个程序占用
nestat -anp | grep 554
输出如下,554被DarwnStreamin程序占用,类型为tcp,状态为listen
tcp 0 0 0.0.0.0:554 0.0.0.0:* LISTEN 2688/DarwinStreamin
- 协议状态统计
netstat -s
可以统计协议的工作状态,但它的输出较多,不便宜观察,推荐使用
ss -s
输出如下:
Total: 508 (kernel 625)
TCP: 80 (estab 51, closed 6, orphaned 0, synrecv 0, timewait 5/0), ports 0
Transport Total IP IPv6
* 625 - -
RAW 1 0 1
UDP 14 10 4
TCP 74 40 34
INET 89 50 39
FRAG 0 0 0
TCP那行里,可以直接看到当前系统所有tcp连接的状态,如已连接状态,close状态等。这样的输出数据简洁明亮,便于观察。
程序调试
-
top + gdb,在前面介绍过通过 top -H -p 命令可以查看指定进程中,线程占用的资源情况。比如观察到占 CPU最高的线程ID后,我们可以通过gdb - pid 直接附加到线程,查看当前线程的堆栈信息,定位程序逻辑问题。
-
perf top,一般在生产环境是不可能用gdb直接去调试程序,这样会造成程序无法处理业务。通过perf工具,可以直接查看系统中消耗性能最大的函数列表。
top + gdb的方式,简单,直接。perf工具提供的分析方式更多,更深入,当然也要求对linux系统知识有更深的理解。