Linux作为一个多任务操作系统,将每个CPU的时间划分成很短的时间片,再通过调度器轮流分配给各个任务使用,因此造成了多个任务同时运行的错觉
为了维护CPU时间,Linux通过实现定义的节拍率(内核中表示为HZ),触发时间中断,并使用全局变量Jiffies记录了开机以来的节拍数,每发生一次时间中断,Jiffies的值就加1
节拍率HZ是内核的可配选项,可以设置为100,250,1000等,不同的系统可能设置不同数值,你可以通过查询
/boot/config 内核选项来查看他的配置值,
比如下面这个,就是每秒有1000个次的时间中断
cat /boot/config-3.10.0-514.16.1.el7.x86_64 | grep HZ
CONFIG_NO_HZ_COMMON=y
# CONFIG_HZ_PERIODIC is not set
# CONFIG_NO_HZ_IDLE is not set
CONFIG_NO_HZ_FULL=y
# CONFIG_NO_HZ_FULL_ALL is not set
CONFIG_NO_HZ=y
# CONFIG_RCU_FAST_NO_HZ is not set
# CONFIG_HZ_100 is not set
# CONFIG_HZ_250 is not set
# CONFIG_HZ_300 is not set
CONFIG_HZ_1000=y
CONFIG_HZ=1000
CONFIG_MACHZ_WDT=m
这个节拍率HZ是内核选项,所以用户空间程序并不能直接访问,为了方便用户空间程序,内核还提供了一个用户空间节拍率 USER_HZ,它总是固定为100,也就是1/100秒
Linux通过/proc 虚拟文件系统,向用户空间提供了系统内部状态的信息,而 /proc/stat 提供的就是系统的CPU和任务统计信息,如果只关注CPU的话,可以执行下面命令
CPU user% nice% system% idle% iowait% irq% softirq% steal% guest_nice%
cpu 46690956 1395 2756716 1100812644 62551 933 216878 35728 0
cpu0 11685110 348 676302 275161847 2032 179 80539 19513 0
cpu1 11655846 375 624830 275424074 1597 65 6651 2351 0
cpu2 11648679 337 680955 275312231 16027 222 36922 3776 0
cpu3 11701320 334 774627 274914491 42894 465 92765 10087 0
每一列的含义如下
1.user(缩写us),表示用户态CPU时间,不包括下面的nice时间,但包括了guest时间
2.nice(缩写ni),表示低优先级用户态CPU时间,进程nice值为1-19之间的CPU时间,-20(最高)到19(最底)
3.sysem(sys),代表内核态CPU时间
4.idle(id)代表空闲时间,但不包括等待I/O的时间
5.iowait(wa),代表等待I/O的CPU时间
6.irq(hi),代表处理硬件中断的CPU时间
7.softirq(si),代表处理软中断的CPU时间
8.steal(st),代表系统运行在虚拟机中的时候,被其他虚拟机占用的CPU时间
9.guest(guest),代表通过虚拟化运行其他操作系统的时间,也就是运行虚拟机的CPU时间
10.guest_nice(gnice),代表以低优先运行虚拟机的时间
我们通常所说的CPU使用率,是除了空闲时间外的其他时间占总CPU时间的百分比,用公式来表示为
根据这个公式,我们就可以从/proc/stat中的数据,计算出CPU使用率,
这个文件中的值就是开机以来的节拍数累加值,直接算出来的,是开机以来的平均CPU使用率
为了计算CPU使用率,性能工具一般都会取间隔一段时间(如3秒)的两次值,做差之后,再计算这段时间内的平均CPU使用率,公式如下
除了/proc/stat,还有/proc/[PID]/stat 这个文件
这两个文件都会被各种系统性能分析工具读取并解析,所以这两个工具是系统指标的来源
性能分析工具给出的都是一段时间的平均CPU使用率,所以要注意间隔时间的设置,特别是用多个工具对比分析时,一定要保证他们用的是相同的间隔时间
比如top默认使用3秒时间间隔,而ps使用的是整个进程的生命周期
除了/proc/stat,还有/proc/[PID]/stat 这个文件
这两个文件都会被各种系统性能分析工具读取并解析,所以这两个工具是系统指标的来源
性能分析工具给出的都是一段时间的平均CPU使用率,所以要注意间隔时间的设置,特别是用多个工具对比分析时,一定要保证他们用的是相同的间隔时间
比如top默认使用3秒时间间隔,而ps使用的是整个进程的生命周期
top显示了系统总体的CPU和内存使用情况,以及各个进程的资源使用情况
ps 则只显示了每个进程的资源使用情况
top的第三行就是CPU的各个指标的使用率情况
pidstat命令显示每个进程的CPU使用率情况
GDB(The GUN Project Debugger),是一个程序调试工具,在调试程序错误方面很强大,但在调试程序的过程中会中断程序运行,在线上环境就不合适了,只适用于性能分析的后期,找到问题的大致函数后,线下再借助这个工具来进一步调试函数内部的问题
perf比较适合在线上环境做性能分析,它以性能事件采样为基础,不仅可以分析系统的各种事件和内核性能,还可以用来分析指定营业程序的性能问题
比如perf top,类似top,能显示占用CPU始终最多的函数或指令,因此可以用来查找热点函数
Samples: 555 of event 'cpu-clock', Event count (approx.): 112367154
Overhead Shared Object Symbol
8.01% [kernel] [k] vsnprintf
4.28% libc-2.17.so [.] __strcmp_sse42
3.75% [kernel] [k] format_decode
3.58% [kernel] [k] __memcpy
3.58% [kernel] [k] kallsyms_expand_symbol.constprop.1
3.07% perf [.] 0x00000000000c53b4
2.85% [kernel] [k] _raw_spin_unlock_irqrestore
2.73% perf [.] __dso__load_kallsyms
2.44% perf [.] rb_next
2.38% perf [.] hex2u64
2.10% [kernel] [k] finish_task_switch
1.87% [kernel] [k] module_get_kallsym
1.87% [kernel] [k] number.isra.2
1.70% [kernel] [k] strnlen
1.70% libc-2.17.so [.] _IO_getdelim
1.70% libc-2.17.so [.] __memcpy_sse2
1.57% libc-2.17.so [.] _int_malloc
1.56% libc-2.17.so [.] __strchr_sse42
1.54% libc-2.17.so [.] __memcpy_ssse3_back
1.53% perf [.] 0x00000000000c53c7
1.42% [kernel] [k] tick_nohz_idle_enter
1.36% [kernel] [k] string.isra.7
1.27% libpthread-2.17.so [.] pthread_rwlock_unlock
1.19% [kernel] [k] pointer.isra.19
1.07% perf [.] rb_insert_color
1.02% libc-2.17.so [.] __strlen_sse2_pminub
0.86% [kernel] [k] run_timer_softirq
0.73% libpthread-2.17.so [.] pthread_rwlock_rdlock
0.73% perf [.] __symbols__insert
0.71% libc-2.17.so [.] __memset_sse2
0.68% [kernel] [k] strlcpy
0.68% libc-2.17.so [.] _IO_feof
0.68% libc-2.17.so [.] memchr
0.68% libpthread-2.17.so [.] pthread_mutex_init
0.68% perf [.] rb_erase
0.67% libc-2.17.so [.] _int_free
0.62% libelf-0.168.so [.] gelf_getsym
0.62% perf [.] symbols__insert
0.61% [kernel] [k] __do_softirq
0.61% [kernel] [k] rcu_gp_kthread
0.56% [kernel] [k] mem_cgroup_charge_common
0.56% perf [.] dso__load_sym
0.51% [kernel] [k] __x86_indirect_thunk_rax
0.51% [kernel] [k] clear_page_c_e
输出结果中,第一行包含三个数据
分别是采样数(Samples),时间类型(event)和事件总数量(Event count)
这里的采样了555个CPU时钟事件,事件总数是112367154个,如果采样数过少(只有十几个)那么排序和百分比意义就不大了
再往下看是一个表格样式数据,每一行包含四列,分别是
1.Overhead, 是该符号的性能事件在所有采样中的比例,用百分比来表示
2.Shared, 是该函数或指令所在的动态共享对象(Dynamic Shared Object)如内核,进程名,动态链接库
的名字,内核模块名字等
3.Object, 是动态共享对象的类型,比如[.]表示用户空间的可执行程序,或动态链接库,而[k]表示
内核空间
4.Symbol, 是符号名,也就是函数名,当函数名未知时,用16进制的地址来表示
perf工具本身也会占用一定的CPU
perf top虽然实时展示了系统的性能信息,但他的缺点是并不保存数据,也就无法用于离线或后续分析,使用
perf record 保存数据,perf report用于后续解析展示
用perf top分析进程的调用栈,但这种方式只适应于c,php等程序,对java并不合适
top -g -p 6492
Samples: 437 of event 'cpu-clock', Event count (approx.): 109250000
Children Self Shared Object Symbol ?- 100.00% 100.00% d [.] d
__libc_start_main
main
a
b
c
d
......
//选择d
Annotate d
?Zoom into d DSO
?Browse map details
?Exit
3a8 400 g _init
? 3e0 3f0 g __libc_start_main@plt
? 3f0 400 g __gmon_start__@plt
? 400 430 g _start
??30 460 l deregister_tm_clones
460 4a0 l register_tm_clones
4a0 4c0 l __do_global_dtors_aux
4c0 4ed l frame_dummy
4ed 4f3 g d
4f3 503 g c
503 513 g b 513 523 g a
523 538 g main
540 5a5 g __libc_csu_init
5b0 5b2 g __libc_csu_fini
??b4 2000 g _fini
关于CPU使用率要关注
用户,nice,系统,等待I/O,中断,软中断,这几个不同的CPU使用率,还要注意
1.用户CPU和nice高,说明用户态进程占用了较多的CPU,应该排查进程的性能问题
2.系统CPU高,说明内核态占用了较多的CPU,应该排查内核线程或者系统调用的性能问题
3.I/O等待CPU高,说明等待I/O的时间比较长,应该排查系统存储是不是出现了I/O问题
4.软中断和硬中断高,说明软中断或硬中断的处理程序占用了较多的CPU,应该排查内核中的中断服务程序
配合top,ps,mpstat,pidstat,vmstat,再结合perf等工具分析
碰到常规问题无法解释的CPU使用率情况时,首先要想到的可能是短时应用导致的问题
1.应用里直接调用了其他二进制程序,这些程序通常运行时间比较短,用top等工具也不容易发现
2.应用本身因为段错误,配置挫等,在不停的时候,但进程退出后又被监控系统自动重启了
使用execsnoop 来监控这种行为,他通过ftrace实时监控进程的exec行为,并输出短时进程的基本信息,包括
pid,父进程id,命令行擦拿上及执行结果等
利用 execsnoop 监控结果(自己写了程序不断fork->exec)
./execsnoop.sh
Tracing exec()s. Ctrl-C to end.
Instrumenting sys_execve
PID PPID ARGS
7935 7931 gawk -v o=1 -v opt_name=0 -v name= -v opt_duration=0 [...]
7936 7934 cat -v trace_pipe
7937 7589 ./fork
8037 7937 fork [?]
8020 7937 fork [?]
8034 7937 fork [?]
8033 7937 fork [?]
8017 7937 fork [?]
7998 7937 fork [?]
.......
参考