Linux 进程管理详解

进程 是 Unix 和 Linux 系统中对正在运行中的应用程序的抽象,通过它可以管理和监视程序对内存、处理器时间和 I / O 资源的使用。

一、进程的组成

一个进程包含内核中的一部分地址空间和一系列数据结构。其中地址空间是内核标记的一部分内存以供进程使用,而数据结构则用来纪录每个进程的具体信息。

最主要的进程信息包括:

  • 进程的地址空间图
  • 进程当前的状态( sleeping、stopped、runnable 等)
  • 进程的执行优先级
  • 进程调用的资源信息
  • 进程打开的文件和网络端口信息
  • 进程的信号掩码(指明哪种信号被屏蔽)
  • 进程的属主
PID :进程 ID

每个进程都会从内核获取一个唯一的 ID 值。绝大多数用来操作进程的命令和系统调用,都需要用 PID 指定操作的进程对象。

PPID :父进程 ID

在 Unix 和 Linux 系统中,一个已经存在的进程必须“克隆”它自身来创建一个新的进程。当新的进程克隆后,最初的进程便作为父进程存在。

UID & EUID:真实用户 ID 和有效用户 ID

一个进程的 UID 是其创建者的身份标志(也是对其父进程 UID 的复制)。通常只有进程的创建者和超级用户才有操作该进程的权限。
EUID 是一个额外的 UID,用来决定在任意一个特定时间点,一个进程有权限访问的文件和资源。对绝大多数进程而言,UID 和 EUID 是相同的(特殊情况即 setuid)

Niceness

一个进程的计划优先级决定了它能获取到的 CPU 时间。内核有一个动态的算法来计算优先级,同时也会关注一个 Niceness 值,来决定程序运行的优先顺序。

二、信号

信号属于进程级别的中断请求。它们可以作为进程间通信的手段,或者由终端发送以杀死、中断、挂起某个进程。

常见信号列表:

# name Description Default Can catch? Can block? Dump core?
1 HUP Hangup Terminate Yes Yes No
2 INT Interrupt(Ctrl + C) Terminate Yes Yes No
3 Quit Quit(Ctrl + \) Terminate Yes Yes Yes
9 KILL Kill Terminate No No No
BUS Bus error Terminate Yes Yes Yes
11 SEGV Segmentation fault Terminate Yes Yes Yes
15 TERM Software terminatation Terminate Yes Yes No
STOP Stop(Ctrl + Z) Stop No No No
TSTP Keyboard stop Stop Yes Yes No
CONT Continue after stop Ignore Yes No No

三、Kill 命令

kill 命令常用来终止某个进程,它可以向进程传递任意信号(默认为 TERM)。
kill [-signal] pid
不带任何数字(信号)选项的 kill 命令并不能保证指定进程被杀死,因为 kill 命令默认发送 TERM 信号,而 TERM 是可以被捕获、屏蔽或忽略的。
可以使用 kill -9 pid 命令强制杀死进程(9 代表 KILL 信号,不可被捕获、屏蔽或忽略)。

kill 命令需要指定进程的 PID 号。
pgrep 命令可以通过程序名称(或其他属性如 UID)筛选进程号,pkill 命令可以直接发送指定信号给筛选结果。
sudo pkill -u ben
该命令将发送 TERM 信号给所有属于用户 ben 的进程。

killall 命令可以通过程序名称杀死指定进程的所有实例。如:
sudo killall apache2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ pgrep postgres  # 筛选 postgres 进程的 PID 号 
25874
25876
25877
25878
25879
25880
25881
$ pgrep -a postgres # 筛选 postgres 进程的 PID 号,并输出详细信息
25874 /usr/lib/postgresql/10/bin/postgres -D /var/lib/postgresql/10/main -c config_file=/etc/postgresql/10/main/postgresql.conf
25876 postgres: 10/main: checkpointer process
25877 postgres: 10/main: writer process
25878 postgres: 10/main: wal writer process
25879 postgres: 10/main: autovacuum launcher process
25880 postgres: 10/main: stats collector process
25881 postgres: 10/main: bgworker: logical replication launcher

$ sudo kill -9 `pgrep postgres` # 杀死 postgres 进程
$ sudo pkill postgres # 同上一条命令
$ sudo killall postgres # 杀死 postgres 进程的所有实例
$ sudo pkill -9 -u postgres # 杀死属于 postgres 用户的所有进程

根据进程 PID 号查找进程可以使用 ps -p <pid> -o comm= 命令

四、进程状态

状态 含义
Runnable 该进程正在(正准备)执行
Sleeping 该进程正等待某些资源
Zombie 该进程正努力尝试结束
Stopped 该进程已挂起(不允许执行)
  • Runnable 表示进程已经获取到了运行所需的所有资源,只是等待相应的 CPU 时间来处理数据。
  • Sleeping 表示进程处于等待特定事件发生的状态。交互式 Shell 和系统守护进程的大部分时间都是 Sleeping 状态,等待用户输入或网络连接。
  • Zombies 表示进程已经结束执行,但是还没有收集完所有的状态,在进程表中仍有纪录。
  • Stopped 表示进程已停止运行,通常是收到了某种停止信号。

五、PS 命令:监控资源

ps 命令是系统管理员监控进程的主要工具。该命令可以显示进程的 PID、UID、优先级和控制终端,以及进程占用的内存、消耗的 CPU 时间和当前的状态等信息。

常用的 PS 命令选项组合:

1. ps aux

a 选项表示显示所有进程,x 选项表示同时显示没有控制终端的进程(TTY 显示为 ?),u 选项表示使用基于用户的信息输出格式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$ ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.1 225428 9548 ? Ss 7月30 0:30 /lib/systemd/systemd --system --deserialize 19
root 2 0.0 0.0 0 0 ? S 7月30 0:00 [kthreadd]
root 4 0.0 0.0 0 0 ? I< 7月30 0:00 [kworker/0:0H]
root 6 0.0 0.0 0 0 ? I< 7月30 0:00 [mm_percpu_wq]
root 7 0.0 0.0 0 0 ? S 7月30 0:03 [ksoftirqd/0]
root 8 0.0 0.0 0 0 ? I 7月30 14:49 [rcu_sched]
...
starky 6874 0.0 0.1 33016 8556 pts/2 Ss 8月07 0:00 bash
starky 7150 0.0 0.0 33016 6044 pts/2 S+ 8月07 0:00 bash
starky 7151 3.1 16.1 4763784 1227932 pts/2 Sl+ 8月07 272:54 java -Xmx1024M -Xms512M -jar minecraft_server.1.12.2.jar nogui
...
root 18447 0.0 0.0 107984 7116 ? Ss 13:55 0:00 sshd: starky [priv]
starky 18535 0.0 0.0 108092 4268 ? S 13:55 0:00 sshd: starky@pts/1
starky 18536 0.0 0.1 33096 8336 pts/1 Ss 13:55 0:00 -bash
root 18761 0.0 0.0 0 0 ? I 13:55 0:00 [kworker/u8:0]
root 18799 0.0 0.0 0 0 ? I 14:01 0:00 [kworker/u8:1]
root 18805 0.0 0.0 0 0 ? I 14:05 0:00 [kworker/0:2]
starky 18874 0.0 0.0 46780 3568 pts/1 R+ 14:10 0:00 ps -aux
redis 19235 0.2 0.0 58548 3736 ? Ssl 8月04 30:03 /usr/bin/redis-server 127.0.0.1:6379
root 20799 0.0 0.0 107548 7504 ? Ss 8月05 0:00 /usr/sbin/cupsd -l
root 28342 0.0 0.4 535068 36940 ? Ss 8月10 0:16 /usr/sbin/apache2 -k start

其中带中括号的命令(如 [kthreadd])并不是真正的命令而是内核线程。

ps aux 命令输出的各列信息含义如下:

项目 解释
USER 进程属主的用户名
PID 进程 ID
%CPU 进程占用的 CPU 百分比
%MEM 进程使用的内存百分比
VSZ 进程的虚拟大小
RSS 驻留内存大小(内存中的页数)
TTY 控制终端 ID
STAT 进程当前的状态:

R = Runnable
D = In uninterruptible sleep
S = Sleeping(<20s)
T = Traced or stopped
Z = Zombie

额外标记:

W = Process is swapped out
< = 进程有相对于平时更高的优先级
N = 进程有相对于平时更低的优先级
L = Some pages are locked in core
s = Process is a session leader
TIME 进程已经消耗的 CPU 时间
COMMAND 进程的命令和命令选项
2. ps lax

l 选项表示以详细的格式输出进程信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
$ ps lax
F UID PID PPID PRI NI VSZ RSS WCHAN STAT TTY TIME COMMAND
4 0 1 0 20 0 225428 9548 - Ss ? 0:30 /lib/systemd/systemd --system --deserialize 19
1 0 2 0 20 0 0 0 - S ? 0:00 [kthreadd]
1 0 4 2 0 -20 0 0 - I< ? 0:00 [kworker/0:0H]
1 0 6 2 0 -20 0 0 - I< ? 0:00 [mm_percpu_wq]
1 0 7 2 20 0 0 0 - S ? 0:03 [ksoftirqd/0]
1 0 8 2 20 0 0 0 - I ? 14:58 [rcu_sched]
...
0 1000 6874 6871 20 0 33016 8556 wait Ss pts/2 0:00 bash
1 1000 7150 6874 20 0 33016 6044 wait S+ pts/2 0:00 bash
0 1000 7151 7150 20 0 4763784 1227932 futex_ Sl+ pts/2 275:03 java -Xmx1024M -Xms512M -jar minecraft_server.1.12.2.jar nogui
...
4 0 18447 619 20 0 107984 7116 - Ss ? 0:00 sshd: starky [priv]
5 1000 18535 18447 20 0 108092 4268 - S ? 0:00 sshd: starky@pts/1
0 1000 18536 18535 20 0 33096 8336 wait Ss pts/1 0:00 -bash
1 0 19051 2 20 0 0 0 - I ? 0:00 [kworker/3:0]
1 0 19141 2 20 0 0 0 - I ? 0:00 [kworker/2:3]
1 115 19235 1 20 0 58548 3736 - Ssl ? 30:22 /usr/bin/redis-server 127.0.0.1:6379
1 0 19246 2 20 0 0 0 - I ? 0:00 [kworker/2:0]
1 0 19291 2 20 0 0 0 - I ? 0:00 [kworker/u8:0]
1 0 19312 2 20 0 0 0 - I ? 0:00 [kworker/0:2]
1 0 19405 2 20 0 0 0 - I ? 0:00 [kworker/u8:1]
0 1000 19417 18536 20 0 36024 1596 - R+ pts/1 0:00 ps -lax
4 0 20799 1 20 0 107548 7504 - Ss ? 0:00 /usr/sbin/cupsd -l
5 0 28342 1 20 0 535068 36940 - Ss ? 0:16 /usr/sbin/apache2 -k start

ps lax 命令的输出包含了父进程 ID(PPID)、nice 值(NI)还有进程正在等待的资源类型(WCHAN)等。

3. ps axjf

ps axjf 命令能够以树状结构显示各进程间的层级关系
f 选项表示用 ASCII 字符显示树状结构,表达程序间的相互关系。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$ ps axjf
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
0 2 0 0 ? -1 S 0 0:00 [kthreadd]
2 4 0 0 ? -1 I< 0 0:00 \_ [kworker/0:0H]
2 6 0 0 ? -1 I< 0 0:00 \_ [mm_percpu_wq]
2 7 0 0 ? -1 S 0 0:02 \_ [ksoftirqd/0]
2 8 0 0 ? -1 I 0 4:26 \_ [rcu_sched]
...
1 672 672 672 ? -1 Ss 0 0:00 /usr/sbin/sshd -D
672 27078 27078 27078 ? -1 Ss 0 0:00 \_ sshd: starky [priv]
27078 27166 27078 27078 ? -1 S 1000 0:00 \_ sshd: starky@pts/1
27166 27167 27167 27167 pts/1 27438 Ss 1000 0:00 \_ -bash
27167 27438 27438 27167 pts/1 27438 R+ 1000 0:00 \_ ps axjf
1 681 681 681 ? -1 Ssl 115 9:40 /usr/bin/redis-server 127.0.0.1:6379
1 700 700 700 tty1 700 Ss+ 0 0:00 /sbin/agetty -o -p -- \u --noclear tty1 linux
1 710 710 710 ? -1 Ss 0 0:14 /usr/sbin/apache2 -k start
710 25651 710 710 ? -1 S 33 0:00 \_ /usr/sbin/apache2 -k start
710 25652 710 710 ? -1 S 33 0:00 \_ /usr/sbin/apache2 -k start
710 25653 710 710 ? -1 S 33 0:00 \_ /usr/sbin/apache2 -k start
710 25654 710 710 ? -1 S 33 0:00 \_ /usr/sbin/apache2 -k start
710 25655 710 710 ? -1 S 33 0:00 \_ /usr/sbin/apache2 -k start
...

4. ps o

ps o 命令加上选项可以指定信息的输出格式,同时加上 --sort 选项可指定排序依据
如:ps axo pid,ppid,%mem,%cpu,cmd --sort=-%mem
上面的命令表示输出进程的 PID、PPID、内存占用、CPU占用和命令选项。并以内存占用大小排序。(--sort=-%mem 中的 - 表示逆向排序,即由大到小排序)

1
2
3
4
5
6
7
8
9
10
11
$ ps axo pid,ppid,%mem,%cpu,cmd --sort=-%mem | head
PID PPID %MEM %CPU CMD
1790 1789 14.1 3.8 java -Xmx1024M -Xms512M -jar minecraft_server.1.12.2.jar nogui
1357 1 2.6 0.1 /usr/sbin/mysqld --daemonize --pid-file=/run/mysqld/mysqld.pid
9343 1 2.0 0.0 /usr/bin/python3 /usr/bin/update-manager --no-update --no-focus-on-map
1244 1 1.5 0.0 sogou-qimpanel %U
1024 1 1.0 0.0 /usr/bin/fcitx
1454 1 0.9 0.0 fcitx-qimpanel
7401 1067 0.7 0.0 lxterminal
248 1 0.6 0.0 /lib/systemd/systemd-journald
1119 1 0.6 0.0 nm-applet

可以尝试不同的命令选项组合来获取相应的信息,具体可参考 man ps

六、使用 TOP 命令动态监控进程

top 命令可以实时显示系统当前活跃进程的总体信息及其占用的资源。
top
top 命令的 -d 选项可以指定信息刷新的时间间隔。同时还有一些常用的交互命令

命令 描述
h 显示帮助信息
k 终止某个进程
i 忽略闲置和僵死进程(这是一个开关式命令)
q 退出 top 程序
r 重新设置某个进程的优先级
s 改变两次刷新之间的延迟时间(单位为s)
f 或 F 从当前显示中添加或者删除项目
l 切换显示平均负载和启动时间信息
m 切换显示内存信息
t 切换显示进程和CPU状态信息
c 切换显示命令名称和完整命令
M 根据驻留内存大小进行排序
P 根据CPU使用百分比大小进行排序
T 根据时间/累计时间进行排序
w 将当前设置写入 ~/.toprc 文件中

七、前台/后台进程

  • 前台进程(也称作交互式进程):由某个终端会话创建和控制的进程。即需要用户控制而不能作为系统服务自动启动。
  • 后台进程(也称作非交互式进程):不和终端绑定的进程,不等待用户输入。

可以在命令后带上 & 符号,在后台启用一个 Linux 进程执行该命令。并通过 jobs 命令查看当前的任务。
使用 fg 命令将后台执行的进程调到前台执行
使用 Ctrl + Z 组合键(发送 SIGSTOP 信号)挂起当前进程(前台),并使用 bg 命令令其在后台继续执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ python -m SimpleHTTPServer &  # 后台启动 python 进程
[1] 28036
$ Serving HTTP on 0.0.0.0 port 8000 ...

$ jobs # 使用 jobs 命令查看后台进行的任务
[1]+ 运行中 python -m SimpleHTTPServer &
$ fg %1 # 将后台执行的第一个任务调到前台执行(fg %1)
python -m SimpleHTTPServer
^Z # 使用 Ctrl + Z 组合键(发送 STOP 信号)停止当前进程
[1]+ 已停止 python -m SimpleHTTPServer
$ bg # 使用 bg 命令将进程调至后台继续执行
[1]+ python -m SimpleHTTPServer &
$ fg %1
python -m SimpleHTTPServer

参考资料

UNIX and Linux System Administration Handbook, 4th Edition
All You Need To Know About Processes in Linux