进程管理¶
在本章中,您将学习如何使用进程。
**目标**: 在本章中,未来的 Linux 管理员将学习如何
识别进程的
PID
和 PPID
; 查看和搜索进程;
管理进程。
进程、Linux
**知识**:
**复杂度**:
**阅读时间**: 20 分钟
概述¶
操作系统由进程组成。这些进程以特定的顺序执行并且相关联。进程有两类,一类侧重于用户环境,另一类侧重于硬件环境。
当程序运行时,系统会通过将程序数据和代码放入内存并创建运行时堆栈来创建进程。进程是程序的一个实例,具有关联的处理器环境(序数计数器、寄存器等)和内存环境。
每个进程都有
- 一个PID: **P*rocess ID*entifier,一个唯一的进程标识符
- 一个PPID: **P*arent Process ID*entifier,父进程的唯一标识符
通过连续的从属关系,init
进程是所有进程的父进程。
- 父进程始终创建进程
- 父进程可以有多个子进程
进程之间存在父子关系。子进程是父进程调用fork()原语并复制其代码以创建子进程的结果。子进程的PID会返回给父进程,以便父进程可以与其通信。每个子进程都有其父进程的标识符,即PPID。
PID 号代表进程执行时的标识符。当进程结束时,该数字将再次可用于另一个进程。多次运行相同的命令会每次产生不同的PID。
注意
进程不应与线程混淆。每个进程都有自己的内存上下文(资源和地址空间),而同一进程的线程共享此上下文。
查看进程¶
ps
命令显示正在运行的进程的状态。
ps [-e] [-f] [-u login]
示例
# ps -fu root
选项 | 描述 |
---|---|
-e | 显示所有进程。 |
-f | 显示完整格式列表。 |
-u 登录名 | 显示用户的进程。 |
一些额外的选项
选项 | 描述 |
---|---|
-g | 显示组中的进程。 |
-t tty | 显示从终端运行的进程。 |
-p PID | 显示进程信息。 |
-H | 以树状结构显示信息。 |
-l | 以长格式显示。 |
--sort COL | 根据列排序结果。 |
--headers | 在每个终端页面上显示标题。 |
--format "%a %b %c" | 自定义输出显示格式。 |
如果没有指定选项,ps
命令只显示从当前终端运行的进程。
结果将显示在以下列中
# ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 Jan01 ? 00:00/03 /sbin/init
列 | 描述 |
---|---|
UID | 所有者用户。 |
PID | 进程标识符。 |
PPID | 父进程标识符。 |
C | 进程的优先级。 |
STIME | 执行日期和时间。 |
TTY | 执行终端。 |
TIME | 处理时间。 |
CMD | 执行的命令。 |
控制的行为可以完全自定义
# ps -e --format "%P %p %c %n" --sort ppid --headers
PPID PID COMMAND NI
0 1 systemd 0
0 2 kthreadd 0
1 516 systemd-journal 0
1 538 systemd-udevd 0
1 598 lvmetad 0
1 643 auditd -4
1 668 rtkit-daemon 1
1 670 sssd 0
进程类型¶
用户进程
- 从与用户关联的终端启动
- 通过请求或守护进程访问资源
系统进程(守护进程)
- 由系统启动
- 不与任何终端相关联,并且由系统用户(通常是
root
)拥有 - 在引导时加载,驻留在内存中,并等待调用
- 通常由与进程名称关联的字母
d
标识
因此,系统进程被称为守护进程(**D*isk **A**nd **E**xecution MON*itor)。
权限和权利¶
执行命令时,用户的凭据会传递给创建的进程。
默认情况下,进程的实际UID
和GID
(进程的)与实际UID
和GID
(执行命令的用户的UID
和GID
)相同。
当在命令上设置SUID
(和/或SGID
)时,实际的UID
(和/或GID
)变为命令的拥有者(和/或拥有者组)的UID
(和/或GID
),不再是发出命令的用户或用户组的UID
(和/或GID
)。因此,有效和实际的UID是不同的。
每次访问文件时,系统都会根据进程的有效标识符检查进程的权限。
进程管理¶
进程不能无限期地运行,因为这会损害其他正在运行的进程,并会阻止多任务处理。
因此,可用的总处理时间被分成多个小范围,每个进程(具有优先级)依次访问处理器。该进程在其生命周期中将经历以下几种状态:
- 就绪:等待进程可用
- 正在执行:访问处理器
- 挂起:等待 I/O(输入/输出)
- 停止:等待来自另一个进程的信号
- 僵尸:请求销毁
- 死亡:父进程结束子进程
进程结束的顺序如下:
- 关闭打开的文件
- 释放使用的内存
- 向父进程和子进程发送信号
当父进程死亡时,它们的子进程被称为孤儿进程。然后它们被init
进程收养,init
进程会销毁它们。
进程的优先级¶
GNU/Linux 属于时间共享操作系统家族。处理器以时间共享的方式工作,每个进程都会占用一些处理器时间。进程按优先级分类:
- 实时进程:优先级为0-99的进程由实时调度算法调度。
- 普通进程:优先级为100-139的进程使用完全公平调度算法调度。
- Nice 值:用于调整普通进程优先级的参数。范围为-20-19。
进程的默认优先级为0。
操作模式¶
进程可以通过两种方式运行:
- 同步:用户在命令执行期间无法访问 shell。命令提示符在进程执行结束时重新出现。
- 异步:进程在后台处理。命令提示符立即显示。
异步模式的限制:
- 命令或脚本不能等待键盘输入
- 命令或脚本不能在屏幕上返回任何结果
- 退出 shell 会结束进程
进程管理控制¶
kill
命令¶
kill
命令向进程发送停止信号。
kill [-signal] PID
示例
kill -9 1664
代码 | 信号 | 描述 |
---|---|---|
2 | SIGINT | 立即终止进程 |
9 | SIGKILL | 中断进程(Ctrl+d) |
15 | SIGTERM | 干净地终止进程 |
18 | SIGCONT | 恢复进程。使用 SIGSTOP 信号的进程可以使用它来继续运行 |
19 | SIGSTOP | 挂起进程(停止进程)。此信号的效果等同于 Ctrl+z |
信号是进程之间通信的方式。kill
命令向进程发送信号。
提示
kill
命令考虑的所有信号的完整列表可以通过输入以下命令获得:
$ man 7 signal
nohup
命令¶
nohup
允许独立于连接启动进程。
nohup command
示例
nohup myprogram.sh 0</dev/null &
nohup
忽略用户注销时发送的SIGHUP
信号。
注意
nohup
处理标准输出和错误,但不处理标准输入,因此将此输入重定向到/dev/null
。
[Ctrl] + [z]¶
通过同时按下 Ctrl+z 键,可以暂时挂起同步进程。在显示刚刚被挂起的进程的编号后,恢复对提示符的访问。
&
指令¶
&
语句异步执行命令(该命令被称为作业),并显示作业的编号。然后返回对提示符的访问。
示例
$ time ls -lR / > list.ls 2> /dev/null &
[1] 15430
$
作业编号是在后台处理期间获得的,并以方括号形式显示,后跟PID
编号。
fg
和 bg
命令¶
fg
命令将进程置于前台
$ time ls -lR / > list.ls 2>/dev/null &
$ fg 1
time ls -lR / > list.ls 2/dev/null
而 bg
命令将进程置于后台
[CTRL]+[Z]
^Z
[1]+ Stopped
$ bg 1
[1] 15430
$
无论是在创建时使用&
参数还是后来使用 Ctrl+z 键将进程置于后台,都可以使用fg
命令和其作业编号将进程带回前台。
jobs
命令¶
jobs
命令显示在后台运行的进程列表,并指定它们的作业编号。
示例
$ jobs
[1]- Running sleep 1000
[2]+ Running find / > arbo.txt
各列代表:
- 作业编号
进程运行的顺序
一个
+
:当未指定作业编号时,fg
和bg
命令默认选择的进程一个
-
:此进程是下一个将获取+
的进程运行(正在运行的进程)或停止(挂起的进程)
- 命令
nice
和 renice
命令¶
nice
命令允许执行命令并指定其优先级。
nice priority command
使用示例
nice --adjustment=-5 find / -name "file"
nice -n -5 find / -name "file"
nice --5 find / -name "file"
nice -n 5 find / -name "file"
nice find / -name "file"
与root
不同,标准用户只能降低进程的优先级,并且只接受 0 到 19 之间的数值。
如上例所示,前三个命令表示将 Nice 值设置为“-5”,而第二个命令是推荐的使用方法。第四个命令表示将 Nice 值设置为“5”。对于第五个命令,不输入任何选项表示 Nice 值设置为“10”。
提示
“Nice”是“niceness”的缩写。
直接输入nice
命令将返回当前 shell 的 Nice 值。
可以通过修改/etc/security/limits.conf
文件来提升每个用户或组的 Nice 值限制。
renice
命令允许更改正在运行的进程的优先级。
renice priority [-g GID] [-p PID] [-u UID]
示例
renice -n 15 -p 1664
选项 | 描述 |
---|---|
-g | 进程拥有者组的GID 。 |
-p | 进程的PID 。 |
-u | 进程拥有者的UID 。 |
renice
命令对现有进程起作用。因此,可以更改特定进程的优先级,以及属于某个用户或组的多个进程的优先级。
提示
pidof
命令与xargs
命令(参见高级命令课程)一起使用,可以使一个命令应用新的优先级
$ pidof sleep | xargs renice -n 20
为了适应不同的发行版,应尽量使用诸如nice -n 5
或renice -n 6
这样的命令形式。
top
命令¶
top
命令显示进程及其资源消耗情况。
$ top
PID USER PR NI ... %CPU %MEM TIME+ COMMAND
2514 root 20 0 15 5.5 0:01.14 top
列 | 描述 |
---|---|
PID | 进程标识符。 |
USER | 所有者用户。 |
PR | 进程优先级。 |
NI | Nice 值。 |
%CPU | 处理器负载。 |
%MEM | 内存负载。 |
TIME+ | 处理器使用时间。 |
COMMAND | 执行的命令。 |
top
命令允许以实时和交互方式控制进程。
pgrep
和 pkill
命令¶
pgrep
命令在正在运行的进程中搜索进程名称,并在标准输出上显示与选择条件匹配的PID。
pkill
命令将向每个进程发送指定的信号(默认情况下为SIGTERM)。
pgrep process
pkill [option] [-signal] process
示例
- 获取
sshd
的进程号
pgrep -u root sshd
- 杀死所有
tomcat
进程
pkill tomcat
注意
在杀死进程之前,最好确切地知道它的用途;否则,会导致系统崩溃或其他不可预测的问题。
除了向相关进程发送信号外,pkill
命令还可以根据终端号结束用户的连接会话,例如:
pkill -t pts/1
killall
命令¶
此命令的功能与pkill
命令大致相同。使用方法为——killall [option] [ -s SIGNAL | -SIGNAL ] NAME
。默认信号为SIGTERM。
选项 | 描述 |
---|---|
-l | 列出所有已知的信号名称 |
-i | 在杀死之前询问确认 |
-I | 不区分大小写的进程名称匹配 |
示例
killall tomcat
pstree
命令¶
此命令以树状方式显示进程,其使用方法为 - pstree [option]
。
选项 | 描述 |
---|---|
-p | 显示进程的 PID |
-n | 按 PID 排序输出 |
-h | 突出显示当前进程及其祖先 |
-u | 显示 uid 转换 |
$ pstree -pnhu
systemd(1)─┬─systemd-journal(595)
├─systemd-udevd(625)
├─auditd(671)───{auditd}(672)
├─dbus-daemon(714,dbus)
├─NetworkManager(715)─┬─{NetworkManager}(756)
│ └─{NetworkManager}(757)
├─systemd-logind(721)
├─chronyd(737,chrony)
├─sshd(758)───sshd(1398)───sshd(1410)───bash(1411)───pstree(1500)
├─tuned(759)─┬─{tuned}(1376)
│ ├─{tuned}(1381)
│ ├─{tuned}(1382)
│ └─{tuned}(1384)
├─agetty(763)
├─crond(768)
├─polkitd(1375,polkitd)─┬─{polkitd}(1387)
│ ├─{polkitd}(1388)
│ ├─{polkitd}(1389)
│ ├─{polkitd}(1390)
│ └─{polkitd}(1392)
└─systemd(1401)───(sd-pam)(1404)
孤儿进程和僵尸进程¶
孤儿进程:当父进程死亡时,它们的子进程被称为孤儿进程。init 进程收养这些特殊状态的进程,并在它们被销毁之前完成状态收集。从概念上讲,孤儿进程不会造成任何危害。
僵尸进程:子进程完成工作并被终止后,其父进程需要调用信号处理函数 wait() 或 waitpid() 来获取子进程的终止状态。如果父进程没有这样做,虽然子进程已经退出,但它仍然在系统进程表中保留了一些退出状态信息。由于父进程无法获取子进程的状态信息,因此这些进程将继续占用进程表中的资源。我们将处于这种状态的进程称为僵尸进程。
危险
- 它们正在占用系统资源并导致机器性能下降。
- 无法生成新的子进程。
我们如何检查当前系统中是否存在任何僵尸进程?
ps -lef | awk '{print $2}' | grep Z
此列中可能会出现以下字符
- D - 不可中断睡眠(通常为 IO)
- I - 空闲内核线程
- R - 正在运行或可运行(在运行队列中)
- S - 可中断睡眠(等待事件完成)
- T - 由作业控制信号停止
- t - 在跟踪期间由调试器停止
- W - 分页(自 2.6.xx 内核起无效)
- X - 已死(不应该出现)
- Z - 无效(“僵尸”)进程,已终止但未被其父进程回收