进程状态
可通过 top 或 ps 查看进程状态。
$ top
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
4480 yjp 20 0 663624 34740 27508 S 0.3 1.9 0:01.26 gnome-term+
5518 yjp 20 0 43536 3768 3232 R 0.3 0.2 0:00.08 top
S 列表示进程的状态。
- R:Running 或 Runnable,进程在 CPU 的就绪队列中,正在运行或等待运行。
- D:Disk Sleep,不可中断状态睡眠,一般表示进程正在与硬件交互,并且交互过程不允许被其它进程中断或打断。
- Z:Zombie,僵尸进程,进程已经结束,但父进程还没有回收其资源,如进程描述符、PID 等。
- S:Interruptible Sleep,可中断状态睡眠,表示进程因为等待某个事件而被挂起。
- I:Idle,空闲状态,用在不可中断睡眠的内核线程上。
- T 或 t:Stoped 或 Traced,表示进程处于暂停或跟踪状态。
案例分析
案例:https://github.com/feiskyer/linux-perf-examples/blob/master/high-iowait-process/README.md
运行案例
docker run --privileged --name=app -itd feisky/app:iowait
确认案例应用启动
$ ps aux | grep /app
root 6051 0.0 0.0 4512 1520 pts/0 Ss+ 19:35 0:00 /app
root 6258 3.5 3.6 70052 66068 pts/0 D+ 19:39 0:00 /app
root 6259 2.0 3.6 70052 66068 pts/0 D+ 19:39 0:00 /app
root 6260 2.0 3.6 70052 66064 pts/0 D+ 19:39 0:00 /app
yjp 6262 0.0 0.0 15960 940 pts/4 S+ 19:39 0:00 grep --color=auto /app
后面的 s 表示该进程是一个会话的领导进程,+ 表示前台进程组。
进程组标表示一组相互关联的进程,比如每个子进程都是父进程所在组的成员。
会话指共享同一个控制终端的一个或多个进程组。
查看系统资源使用情况
# 按下数字 1 切换到所有 CPU 的使用情况,观察一会儿按 Ctrl+C 结束
$ top
top - 19:41:10 up 2:48, 1 user, load average: 2.68, 2.26, 1.07
Tasks: 324 total, 1 running, 179 sleeping, 0 stopped, 144 zombie
%Cpu0 : 1.8 us, 3.3 sy, 0.0 ni, 27.6 id, 65.4 wa, 0.0 hi, 1.8 si, 0.0 st
%Cpu1 : 2.1 us, 5.1 sy, 0.0 ni, 35.2 id, 56.4 wa, 0.0 hi, 1.3 si, 0.0 st
KiB Mem : 1789476 total, 479536 free, 732760 used, 577180 buff/cache
KiB Swap: 1533948 total, 1526512 free, 7436 used. 836700 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
6308 root 20 0 0 0 0 Z 7.5 0.0 0:00.31 app
4066 yjp 20 0 1247376 81264 47124 S 4.9 4.5 1:27.33 compiz
1449 root 20 0 455808 69676 31716 S 4.6 3.9 0:52.98 Xorg
6307 root 20 0 0 0 0 Z 2.9 0.0 0:00.21 app
6309 yjp 20 0 43668 3972 3432 R 1.6 0.2 0:00.08 top
6310 root 20 0 70052 66072 612 D 0.7 3.7 0:00.02 app
6311 root 20 0 70052 66072 612 D 0.7 3.7 0:00.02 app
- 第一行,平均负载在升高,而 1 分钟的平均负载已超 CPU 个数,说明系统可能有了性能瓶颈。
- 第二行的 Tasks,有 1 个正在运行的进程,但僵尸进程比较多,且在增加,说明有子进程退出时没被清理。
- 第三行,用户 CPU 和系统 CPU 都不高,但 iowait 分别是 65.4%,56.4%
- 有两个进程处于 D 状态,可能在等待 I/O
iowait 分析
# 间隔 1 秒输出 10 组数据
$ dstat 1 10
You did not select any stats, using -cdngy by default.
----total-cpu-usage---- -dsk/total- -net/total- ---paging-- ---system--
usr sys idl wai hiq siq| read writ| recv send| in out | int csw
8 1 83 8 0 0| 45M 66k| 0 0 | 27B 704B| 352 698
1 3 59 38 0 0| 59M 0 | 0 278B| 0 0 | 451 800
1 2 0 97 0 0| 104M 0 | 60B 60B| 0 0 | 561 927
1 2 0 90 0 7| 636M 48k| 0 0 | 0 0 |1319 1094
2 6 11 73 0 8| 548M 0 | 0 107B| 0 0 |1291 777
4 8 27 47 0 13| 749M 0 | 0 0 | 0 0 |1938 957
每当 iowait 升高(wai)时,磁盘的读请求(read)都会很大,可能是磁盘读请求导致。
查找读磁盘的进程
$ top
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
7563 root 20 0 0 0 0 Z 4.3 0.0 0:00.25 app
7564 root 20 0 0 0 0 Z 4.0 0.0 0:00.16 app
16 root 20 0 0 0 0 S 3.3 0.0 0:17.35 ksoftirqd/1
1449 root 20 0 459928 73636 31716 S 3.0 4.1 1:05.10 Xorg
4066 yjp 20 0 1247376 81800 47236 S 3.0 4.6 1:47.70 compiz
7566 root 20 0 70052 65968 476 D 1.7 3.7 0:00.05 app
7567 root 20 0 70052 65968 476 D 1.3 3.7 0:00.04 app
有两个 D 状态的进程,PID 分别为 7566 和 7567。
# -d 展示 I/O 统计数据,-p 指定进程号,间隔 1 秒输出 3 组数据
$ pidstat -d -p 7566 1 3
Linux 4.13.0-45-generic (yjp-VirtualBox) 2019年04月28日 _x86_64_ (2 CPU)
20时00分53秒 UID PID kB_rd/s kB_wr/s kB_ccwr/s iodelay Command
20时00分54秒 0 7566 0.00 0.00 0.00 0 app
20时00分55秒 0 7566 0.00 0.00 0.00 0 app
20时00分56秒 0 7566 0.00 0.00 0.00 0 app
Average: 0 7566 0.00 0.00 0.00 0 app
但发现进程 7566 没有 I/O 读写,进程 7567 同样。
干脆查看所有进程的 I/O 读写:
# 间隔 1 秒输出多组数据 (这里是 20 组)
$ pidstat -d 1 20
...
06:48:46 UID PID kB_rd/s kB_wr/s kB_ccwr/s iodelay Command
06:48:47 0 4615 0.00 0.00 0.00 1 kworker/u4:1
06:48:47 0 6080 32768.00 0.00 0.00 170 app
06:48:47 0 6081 32768.00 0.00 0.00 184 app
06:48:47 UID PID kB_rd/s kB_wr/s kB_ccwr/s iodelay Command
06:48:48 0 6080 0.00 0.00 0.00 110 app
06:48:48 UID PID kB_rd/s kB_wr/s kB_ccwr/s iodelay Command
06:48:49 0 6081 0.00 0.00 0.00 191 app
06:48:49 UID PID kB_rd/s kB_wr/s kB_ccwr/s iodelay Command
06:48:50 UID PID kB_rd/s kB_wr/s kB_ccwr/s iodelay Command
06:48:51 0 6082 32768.00 0.00 0.00 0 app
06:48:51 0 6083 32768.00 0.00 0.00 0 app
06:48:51 UID PID kB_rd/s kB_wr/s kB_ccwr/s iodelay Command
06:48:52 0 6082 32768.00 0.00 0.00 184 app
06:48:52 0 6083 32768.00 0.00 0.00 175 app
06:48:52 UID PID kB_rd/s kB_wr/s kB_ccwr/s iodelay Command
06:48:53 0 6083 0.00 0.00 0.00 105 app
...
是 app 进行磁盘读写,查看其系统调用:
$ strace -p 6082
strace: attach: ptrace(PTRACE_SEIZE, 6082): Operation not permitted
已经是 root 权限了,可能是进程状态不正常:
$ ps aux | grep 6082
root 6082 0.0 0.0 0 0 pts/0 Z+ 13:43 0:00 [app] <defunct>
使用基于事件记录的动态工具:
$ perf record -g
$ perf report
app 通过 sys_read 读数据,从 new_sync_read 和 blkdev_direct_IO 看出,对磁盘进行直接读,导致 iowait 升高。
查看源码 app.c:
open(disk, O_RDONLY|O_DIRECT|O_LARGEFILE, 0755)
删除 O_DIRECT 即可。
重新运行:
# 首先删除原来的应用
$ docker rm -f app
# 运行新的应用
$ docker run --privileged --name=app -itd feisky/app:iowait-fix1
检查:
$ top
top - 14:59:32 up 19 min, 1 user, load average: 0.15, 0.07, 0.05
Tasks: 137 total, 1 running, 72 sleeping, 0 stopped, 12 zombie
%Cpu0 : 0.0 us, 1.7 sy, 0.0 ni, 98.0 id, 0.3 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu1 : 0.0 us, 1.3 sy, 0.0 ni, 98.7 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
...
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
3084 root 20 0 0 0 0 Z 1.3 0.0 0:00.04 app
3085 root 20 0 0 0 0 Z 1.3 0.0 0:00.04 app
1 root 20 0 159848 9120 6724 S 0.0 0.1 0:09.03 systemd
2 root 20 0 0 0 0 S 0.0 0.0 0:00.00 kthreadd
3 root 20 0 0 0 0 I 0.0 0.0 0:00.40 kworker/0:0
...
僵尸进程
僵尸进程是因为父进程没有回收子进程资源导致的,因此需找出父进程。
# -a 表示输出命令行选项
# p 表 PID
# s 表示指定进程的父进程
$ pstree -aps 3084
systemd,1
└─dockerd,15006 -H fd://
└─docker-containe,15024 --config /var/run/docker/containerd/containerd.toml
└─docker-containe,3991 -namespace moby -workdir...
└─app,4009
└─(app,3084)
查看 app.c:
int status = 0;
for (;;) {
for (int i = 0; i < 2; i++) {
if(fork()== 0) {
sub_process();
}
}
sleep(5);
}
while(wait(&status)>0);
将 wait() 放在 for 死循环内即可。
然后重新启动容器,并用 top 进行检查:
# 先停止产生僵尸进程的 app
$ docker rm -f app
# 然后启动新的 app
$ docker run --privileged --name=app -itd feisky/app:iowait-fix2
$ top
top - 15:00:44 up 20 min, 1 user, load average: 0.05, 0.05, 0.04
Tasks: 125 total, 1 running, 72 sleeping, 0 stopped, 0 zombie
%Cpu0 : 0.0 us, 1.7 sy, 0.0 ni, 98.3 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu1 : 0.0 us, 1.3 sy, 0.0 ni, 98.7 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
...
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
3198 root 20 0 4376 840 780 S 0.3 0.0 0:00.01 app
2 root 20 0 0 0 0 S 0.0 0.0 0:00.00 kthreadd
3 root 20 0 0 0 0 I 0.0 0.0 0:00.41 kworker/0:0
...
参考
倪朋飞. Linux 性能优化实战.