linux僵尸进程有io阻塞,Linux 性能优化实战(倪朋飞)---系统中出现大量不可中断进程和僵尸进程怎么办?...

进程状态

可通过 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]

使用基于事件记录的动态工具:

$ perf record -g

$ perf report

3a9027043dd5c9272611e4f44868b23d.png

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 性能优化实战.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值