estado del proceso
A través del comando superior, podemos ver el estado del proceso (columna S)
top
top - 19:27:57 up 365 days, 25 min, 0 users, load average: 0.06, 0.05, 0.01
Tasks: 134 total, 1 running, 90 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.3 us, 0.5 sy, 0.0 ni, 99.2 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 3514764 total, 137136 free, 1193824 used, 2183804 buff/cache
KiB Swap: 0 total, 0 free, 0 used. 1965284 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
11399 root 20 0 1114904 150016 19564 S 1.0 4.3 537:19.31 YDService
9951 ubuntu 20 0 1142180 261916 36424 S 0.7 7.5 0:44.05 node
15178 ubuntu 20 0 41144 3700 3032 R 0.3 0.1 0:00.06 top
30256 root 20 0 64552 11232 3632 S 0.3 0.3 41:52.93 barad_agent
30257 root 20 0 588648 20356 4840 S 0.3 0.6 316:58.21 barad_agent
1 root 20 0 225544 7596 4920 S 0.0 0.2 19:35.77 systemd
2 root 20 0 0 0 0 S 0.0 0.0 0:13.93 kthreadd
4 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 kworker/0:0H
El estado del proceso es el siguiente:
-
R es la abreviatura de Running o Runnable, lo que significa que el proceso se está ejecutando o esperando para ejecutarse en la cola lista de la CPU.
-
D es la abreviatura de Disk Sleep, es decir, Uninterruptible Sleep (Sueño ininterrumpido), lo que generalmente significa que el proceso está interactuando con el hardware, y no se permite que el proceso de interacción sea interrumpido por otros procesos o interrupciones.
-
Z es la abreviatura de Zombie, es decir, el estado zombie. El proceso en realidad ha terminado, pero el proceso padre no ha reclamado sus recursos (como el descriptor de proceso, PID, etc.).
-
S es la abreviatura de Interruptible Sleep, es decir, estado de suspensión interrumpible, lo que significa que el sistema suspende el proceso porque está esperando un evento. Cuando ocurre el evento que el proceso está esperando, se despierta y entra en el estado R.
-
I es la abreviatura de Idle, es decir, el estado inactivo, que se utiliza en subprocesos del núcleo que no pueden interrumpir el sueño. Como se mencionó anteriormente, el proceso ininterrumpido causado por la interacción del hardware está representado por D, pero para algunos subprocesos del kernel, es posible que en realidad no tengan ninguna carga, y se usa Idle para distinguir esta situación. Tenga en cuenta que los procesos en el estado D aumentarán el promedio de carga, mientras que los procesos en el estado I no lo harán.
-
T o t, que es la abreviatura de Stopped or Traced, significa que el proceso está en estado suspendido o rastreado. Envíe una señal SIGSTOP a un proceso y se suspenderá (Detenido) en respuesta a esta señal; envíele una señal SIGCONT y el proceso reanudará su ejecución. Y cuando usa un depurador (como gdb) para depurar un proceso, después de usar un punto de interrupción para interrumpir el proceso, el proceso se convertirá en un estado de seguimiento, que en realidad es un estado de pausa especial, pero puede usar el depurador para realizar un seguimiento. controlar la operación del proceso según sea necesario.
-
X, abreviatura de Dead, significa que el proceso está muerto, por lo que no lo verá en los comandos top o ps.
estado ininterrumpido
Primero mire el estado ininterrumpible, que en realidad es para garantizar que los datos del proceso sean consistentes con el estado del hardware y, en circunstancias normales, el estado ininterrumpible finalizará en poco tiempo. Por lo tanto, generalmente podemos ignorar el proceso de estado ininterrumpible a corto plazo.
但如果系统或硬件发生了故障,进程可能会在不可中断状态保持很久,甚至导致系统中出现大量不可中断进程。这时,你就得注意下,系统是不是出现了 I/O 等性能问题。
僵尸进程
僵尸进程,这是多进程应用很容易碰到的问题。正常情况下,当一个进程创建了子进程后,它应该通过系统调用 wait() 或者 waitpid() 等待子进程结束,回收子进程的资源;而子进程在结束时,会向它的父进程发送 SIGCHLD 信号,所以,父进程还可以注册 SIGCHLD 信号的处理函数,异步回收资源。
如果父进程没这么做,或是子进程执行太快,父进程还没来得及处理子进程状态,子进程就已经提前退出,那这时的子进程就会变成僵尸进程。
通常,僵尸进程持续的时间都比较短,在父进程回收它的资源后就会消亡;或者在父进程退出后,由 init 进程回收后也会消亡。一旦父进程没有处理子进程的终止,还一直保持运行状态,那么子进程就会一直处于僵尸状态。
大量的僵尸进程会用尽 PID 进程号,导致新进程不能创建,所以这种情况一定要避免。
iowait 分析
我们先用dstat分析一下数据
# 间隔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 stl| read writ| recv send| in out | int csw
0 0 99 0 0|3880B 47k| 0 0 | 0 0 | 379 1240
1 2 98 0 0| 0 0 |1332B 2039B| 0 0 | 884 1683
2 3 95 0 0| 0 0 |1158B 3065B| 0 0 |1066 2113
2 1 96 1 0| 0 384k|2548B 3323B| 0 0 |1018 1991
0 0 100 0 0| 0 0 | 364B 380B| 0 0 | 616 1166
1 0 99 0 0| 0 0 | 986B 1016B| 0 0 | 739 1432
1 1 98 0 0| 0 0 |1304B 5305B| 0 0 | 775 1659
1 1 98 0 0| 0 0 |1950B 909B| 0 0 | 764 1450
1 1 98 0 0| 0 68k|2898B 4832B| 0 0 | 799 1423
1 1 98 0 0| 0 0 | 856B 1088B| 0 0 | 667 1382
pidstat 查看IO
# -d 展示 I/O 统计数据,-p 指定进程号,间隔 1 秒输出 10 组数据
pidstat -d -p 9951 1 10
Linux 4.15.0-180-generic (VM-0-11-ubuntu) 08/04/2023 _x86_64_ (2 CPU)
07:40:23 PM UID PID kB_rd/s kB_wr/s kB_ccwr/s iodelay Command
07:40:24 PM 500 9951 0.00 0.00 0.00 0 node
07:40:25 PM 500 9951 0.00 0.00 0.00 0 node
07:40:26 PM 500 9951 0.00 0.00 0.00 0 node
07:40:27 PM 500 9951 0.00 0.00 0.00 0 node
07:40:28 PM 500 9951 0.00 0.00 0.00 0 node
07:40:29 PM 500 9951 0.00 0.00 0.00 0 node
07:40:30 PM 500 9951 0.00 0.00 0.00 0 node
07:40:31 PM 500 9951 0.00 0.00 0.00 0 node
07:40:32 PM 500 9951 0.00 0.00 0.00 0 node
07:40:33 PM 500 9951 0.00 0.00 0.00 0 node
Average: 500 9951 0.00 0.00 0.00 0 node
如果无法定位,可以去掉进程号看所有的进程,观察现象
# 间隔 1 秒输出多组数据 (这里是 20 组)
pidstat -d 1 20
通过 strace 进行跟踪
上一步拿到 pid 后可以用 strace 进行跟踪
sudo strace -p 11399
strace: Process 11399 attached
epoll_wait(11, [{EPOLLIN, {u32=40982992, u64=40982992}}], 16, 120000) = 1
read(13, ".\0\0\0(\0\0\0X\10\20\20\1j 1a44f465a3554d4f9"..., 262144) = 138
read(13, 0x7fff7523a380, 262144) = -1 EAGAIN (Resource temporarily unavailable)
nanosleep({tv_sec=0, tv_nsec=10000000}, NULL) = 0
epoll_wait(11, [{EPOLLIN, {u32=40982992, u64=40982992}}], 16, 120000) = 1
read(13, ".\0\0\0(\0\0\0X\10\20\20\1j 2b7e857880da41f8b"..., 262144) = 138
read(13, 0x7fff7523a380, 262144) = -1 EAGAIN (Resource temporarily unavailable)
nanosleep({tv_sec=0, tv_nsec=10000000}, NULL) = 0
epoll_wait(11,
ps检查进程状态
ps aux | grep 11399
root 11399 0.7 4.2 1114904 149348 ? Sl Jun13 537:27 /usr/local/qcloud/YunJing/YDEyes/YDService
ubuntu 23898 0.0 0.0 13776 1104 pts/4 S+ 19:46 0:00 grep --color=auto 11399
如果以上方案都不行,就要用基于事件记录的动态追踪工具。
perf top 或 perf record
pref top -g
pref record -g
pref report
处理僵尸进程
找僵尸进程父进程
# -a 表示输出命令行选项
# p表PID
# s表示指定进程的父进程
pstree -aps 11399
systemd,1 --switched-root --system --deserialize 32
└─YDLive,7059
└─YDService,11399
├─sh,11521 -c sleep 100
│ ├─{sh},11523
│ ├─{sh},11524
│ ├─{sh},11525
│ ├─{sh},11526
│ ├─{sh},11527
│ ├─{sh},11528
│ ├─{sh},11530
│ └─{sh},9989
├─{YDService},11400
├─{YDService},11401
├─{YDService},11402
├─{YDService},11403
├─{YDService},11404
├─{YDService},11405
├─{YDService},11406
├─{YDService},11407
├─{YDService},11408
├─{YDService},11428
├─{YDService},11434
├─{YDService},11450
├─{YDService},11451
├─{YDService},11455
├─{YDService},11456
├─{YDService},11457
├─{YDService},11466
├─{YDService},11467
├─{YDService},11468
├─{YDService},11483
├─{YDService},11485
├─{YDService},11522
└─{YDService},4319
再处理父进程就可以了。
小结
iowait 高不一定代表 I/O 有性能瓶颈。当系统中只有 I/O 类型的进程在运行时,iowait 也会很高,但实际上,磁盘的读写远没有达到性能瓶颈的程度。
因此,碰到 iowait 升高时,需要先用 dstat、pidstat 等工具,确认是不是磁盘 I/O 的问题,然后再找是哪些进程导致了 I/O。
等待 I/O 的进程一般是不可中断状态,所以用 ps 命令找到的 D 状态(即不可中断状态)的进程,多为可疑进程。有时在 I/O 操作后,进程又变成了僵尸进程,所以不能用 strace 直接分析这个进程的系统调用。这种情况下,需要用 perf 工具,来分析系统的 CPU 时钟事件,最终发现是直接 I/O 导致的问题。这时,再检查源码中对应位置的问题,就很轻松了。而僵尸进程的问题相对容易排查,使用 pstree 找出父进程后,去查看父进程的代码,检查 wait() / waitpid() 的调用,或是 SIGCHLD 信号处理函数的注册就行了。