跳转至

进程管理

在本章中,您将学习如何使用进程。


目标:在本章中,未来的 Linux 管理员将学习如何:

✔ 识别进程的 PIDPPID
✔ 查看并搜索进程;
✔ 管理进程。

🏁 进程linux

知识性: ⭐ ⭐
复杂度: ⭐

阅读时间: 20 分钟


概述

操作系统由进程组成。 这些进程以特定的顺序执行并且彼此相关。 有两大类的进程,一类侧重于用户环境,另一类侧重于硬件环境。

当程序运行时,系统将程序数据和代码放入到内存中并创建 运行栈 来创建进程。 因此,进程是程序的一个实例,它具有相关联的处理器环境(顺序计数器、寄存器等)和内存环境。

每个进程都有:

  • PIDProcess IDentifier,唯一的进程标识符
  • PPIDParent Process IDentifier,父进程的唯一标识符

通过连续的隶属关系,init 进程是所有进程之父。

  • 一个进程始终由父进程创建
  • 一个父进程可以有多个子进程

进程之间存在父/子关系。 子进程是父进程调用 fork() 原语并复制自己的代码来创建子进程的结果。 子进程的 PID 会返回给父进程,以便父进程与之对话。 每个子进程都有父进程的标识符 PPID

PID 数字代表执行时的进程。 当进程结束时,该数字可再次用于另一个进程。 多次运行同一命令将每次产生不同的 PID!!! note "说明"

请不要将进程与 _线程_ 混淆。 每个进程都有自己的内存上下文(资源和地址空间),而来自同一进程的 _线程_ 则 共享相同的上下文。

查看进程

ps 命令显示正在运行的进程的状态。

ps [-e] [-f] [-u login]

示例:

# ps -fu root

选项 说明
-e 显示所有进程。
-f 显示附加信息。
-u login 显示用户的进程。

一些附加选项:

选项 说明
-g 显示组中的进程。
-t tty 显示从终端运行的进程。
-p PID 显示进程信息。
-H 以树结构显示信息。
-I 显示附加信息。
--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 来标识

因此,系统进程被称为守护进程((Disk And Execution MONitor

权限

执行命令时,用户的凭据将传递给创建的进程。

因此,在默认情况下,进程的实际 UID<code> 和 <code>GID 与 执行命令的用户的 UIDGID 相同。

在命令上设置 SUID(和/或 SGID)后,实际 UID(和/或 GID)将变为命令所有者(和/或所属组)的 UID(和/或 GID),而不再是发出命令的用户或用户组的 UID(和/或 GID)。 因此,有效的 UID 和真实的 UID 是不同的。

每次访问文件时,系统都会根据其有效标识符检查进程的权限。

进程管理

一个进程不能无限期运行,因为这会损害其他正在运行的进程,并会妨碍多任务处理。

因此,将可用的总处理时间划分为几个小范围,每个具有优先级的进程按顺序访问处理器。 进程在其生命周期中将经历几个状态:

  • 就绪:等待进程可用
  • 执行中:访问处理器
  • 挂起:等待 I/O(输入/输出)
  • 停止:等待来自另一个进程的信号
  • 僵尸:请求销毁
  • 死亡:父进程结束子进程

进程结束排序如下:

  1. 关闭打开的文件
  2. 释放使用过的内存
  3. 向父进程和子进程发送信号

当父进程死亡时,其子进程被称为孤儿进程。 然后它们被 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 恢复进程
19 SIGSTOP 挂起进程

信号是进程之间的通信手段。 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 编号。

fgbg 命令

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 命令

job 命令显示后台运行的进程列表,并指定它们的作业编号。

示例:

$ jobs
[1]- Running    sleep 1000
[2]+ Running    find / > arbo.txt

这些列表示:

  1. 作业编号
  2. 进程运行的顺序
  3. +:未指定作业编号时,fgbg 命令默认选择的进程
  4. -:下一个进程选择 +
  5. 正在运行(正在运行的进程)或_已停止_(已挂起的进程)
  6. 命令

nicerenice 命令

命令 nice 允许通过指定优先级来执行命令。

nice priority command

示例:

$ nice -n+15 find / -name "file"

root 用户不同,标准的普通用户只能降低进程的优先级。 只接受 +0 到 +19 之间的值。

提示

通过修改 /etc/security/limits.conf 文件,可以针对每个用户或每个组取消最后这项限制。

使用 renice 命令可以更改正在运行中的进程优先级。

renice priority [-g GID] [-p PID] [-u UID]

示例:

$ renice +15 -p 1664
| 选项 | 说明 | | ---- | ------------- | | -g | 进程所属组的 GID。 | | -p | 进程的 PID。 | | -u | 进程所有者的 UID。 |

renice 命令作用于已经运行的进程。 因此,我们可以改变特定进程的优先级,也可以改变属于用户或组的几个进程的优先级。

提示

pidof 命令与 xargs 命令相结合(请参阅 "高级命令" 课程),可允许在单个命令中应用新的优先级:

$ pidof sleep | xargs renice 20

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 命令允许以交互模式实时控制进程。

pgreppkill 命令

pgrep 命令可在运行的进程中搜索进程名,并在标准输出中显示与选择条件匹配的 PID

pkill 命令将向每个进程发送指定的信号(默认情况下为 SIGTERM)。

pgrep process
pkill [-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 - 已失效的进程("僵尸"),已终止但未被其父进程获取