phper-linux-gitbook/zombie-process.md
2020-01-21 11:26:58 +08:00

81 lines
7.1 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

## 什么是不可中断状态
当 iowait 升高时,进程很可能因为得不到硬件的响应,而长时间处于不可中断状态。从 ps 或者 top 命令的输出中,可以发现它们都处于 D 状态,也就是不可中断状态 Uninterruptible Sleep
不可中断状态,表示进程正在跟硬件交互,为了保护进程数据和硬件的一致性,系统不允许其他进程或中断打断这个进程。进程长时间处于不可中断状态,通常表示系统有 I/O 性能问题。
中断其实是一种异步的事件处理机制,可以提高系统的并发处理能力。
由于中断处理程序会打断其他进程的运行,所以,为了减少对正常进程运行调度的影响,中断处理程序就需要尽可能快地运行。如果中断本身要做的事情不多,那么处理起来也不会有太大问题;但如果中断要处理的事情很多,中断服务程序就有可能要运行很长时间。
特别是,中断处理程序在响应中断时,还会临时关闭中断。这就会导致上一次中断处理完成之前,其他中断都不能响应,也就是说中断有可能会丢失。
举个最常见的网卡接收数据包的例子:网卡接收到数据包后,会通过 __硬件中断__ 的方式,通知内核有新的数据到了。这时,内核就应该调用中断处理程序来响应它。对上半部来说,既然是快速处理,其实就是要把网卡的数据读到内存中,然后更新一下硬件寄存器的状态(表示数据已经读好了),最后再发送一个 __软中断__ 信号,通知下半部做进一步的处理。而下半部被软中断信号唤醒后,需要从内存中找到网络数据,再按照网络协议栈,对数据进行逐层解析和处理,直到把它送给应用程序。
Linux 将中断处理过程分成了两个阶段,也就是上半部和下半部:
- 上半部直接处理硬件请求,也就是我们常说的硬中断,特点是快速执行;
- 而下半部则是由内核触发,也就是我们常说的软中断,特点是延迟执行。
Linux 中的软中断包括网络收发、定时、调度、RCU 锁等各种类型,可以通过查看 /proc/softirqs 来观察软中断的运行情况。
## 什么是僵死(僵尸)进程
僵死(僵尸)进程:一个进程使用 fork 创建子进程,如果子进程退出,而父进程并没有调用 wait() 或 waitpid() 获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死(僵尸)。
通常,僵尸进程持续的时间都比较短,在父进程回收它的资源后就会消亡;或者在父进程退出后,由 init 进程回收后也会消亡。
短暂的僵尸状态我们通常不必理会,但进程长时间处于僵尸状态,就应该注意了,可能有应用程序没有正常处理子进程的退出。
#### top 命令查看进程状态
top 和 ps 是最常用的查看进程状态的工具,我们就从 top 的输出开始。下面是一个 top 命令输出的示例S 列(也就是 Status 列)表示进程的状态。
```
top - 21:43:31 up 28 days, 23:44, 2 users, load average: 0.02, 0.02, 0.00
Tasks: 139 total, 1 running, 138 sleeping, 0 stopped, 0 zombie
%Cpu(s): 3.2 us, 1.2 sy, 0.0 ni, 95.7 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 4042140 total, 1234472 free, 525152 used, 2282516 buff/cache
KiB Swap: 0 total, 0 free, 0 used. 3169024 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
4606 rabbitmq 20 0 2169848 69784 6516 S 0.3 1.7 115:20.52 beam.smp
4921 rabbitmq 20 0 7716 88 0 S 0.3 0.0 0:05.35 inet_gethost
11943 ubuntu 20 0 40504 3764 3184 R 0.3 0.1 0:00.01 top
1 root 20 0 119960 6116 4020 S 0.0 0.2 3:18.65 systemd
2 root 20 0 0 0 0 S 0.0 0.0 0:00.00 kthreadd
3 root 20 0 0 0 0 S 0.0 0.0 0:33.16 ksoftirqd/0
```
- __R__ 是 Running 或 Runnable 的缩写,表示进程在 CPU 的就绪队列中,正在运行或者正在等待运行。
- __D__ 是 Disk Sleep 的缩写也就是不可中断状态睡眠Uninterruptible Sleep一般表示进程正在跟硬件交互并且交互过程不允许被其他进程或中断打断。
- __Z__ 是 Zombie 的缩写,如果你玩过“植物大战僵尸”这款游戏,应该知道它的意思。它 表示僵尸进程也就是进程实际上已经结束了但是父进程还没有回收它的资源比如进程的描述符、PID 等)。
- __S__ 是 Interruptible Sleep 的缩写,也就是可中断状态睡眠,表示进程因为等待某个事件 而被系统挂起。当进程等待的事件发生时,它会被唤醒并进入 R 状态。
- __I__ 是 Idle 的缩写,也就是空闲状态,用在不可中断睡眠的内核线程上。前面说了,硬件交互导致的不可中断进程用 D 表示,但对某些内核线程来说,它们有可能实际上并没有任何负载,用 Idle 正是为了区分这种情况。要注意D 状态的进程会导致平均负载升高, I 状态的进程却不会。
- __T__ 或者 t也就是 Stopped 或 Traced 的缩写,表示进程处于暂停或者跟踪状态。向一个进程发送 SIGSTOP 信号它就会因响应这个信号变成暂停状态Stopped再向它发送 SIGCONT 信号,进程又会恢复运行(如果进程是终端里直接启动的,则需要你 用 fg 命令,恢复到前台运行)。而当你用调试器(如 gdb调试一个进程时在使用断点中断进程后进程就会变成跟踪状态这其实也是一种特殊的暂停状态只不过你可以用调试器来跟踪并按需要控制进程的运行。
- __X__ 是 Dead 的缩写,表示进程已经消亡,所以你不会在 top 或者 ps 命令中看到它。
#### ps 命令查看进程状态
```
root 6525 0.0 0.1 65512 6088 ? Ss Jun12 12:22 /usr/sbin/sshd -D
root 10376 0.0 0.1 99272 6944 ? Ss 21:25 0:00 sshd: ubuntu [priv]
ubuntu 10453 0.0 0.0 99272 3384 ? S 21:25 0:00 sshd: ubuntu@pts/0
root 11893 0.0 0.1 99272 6920 ? Ss 21:43 0:00 sshd: ubuntu [priv]
ubuntu 11928 0.0 0.0 99272 3348 ? S 21:43 0:00 sshd: ubuntu@pts/1
root 12913 0.0 0.0 12944 928 pts/1 S+ 21:55 0:00 grep --color=auto sshd
```
状态为 Ss+ 和 D+ ,其中 S 表示可中断睡眠状态D 表示不可中断睡眠状态。s 表示 这个进程是一个会话的领导进程,而 + 表示前台进程组。
进程组和会话。它们用来管理一组相互关联的进程,进程组表示一组相互关联的进程,比如每个子进程都是父进程所在组的成员;而会话是指共享同一个控制终端的一个或多个进程组。
比如,我们通过 SSH 登录服务器就会打开一个控制终端TTY这个控制终端就对应 一个会话。而我们在终端中运行的命令以及它们的子进程,就构成了一个个的进程组,其中,在后台运行的命令,构成后台进程组;在前台运行的命令,构成前台进程组。