如何使用Bash的作业控制来管理前台和后台进程

来自菜鸟教程
跳转至:导航、​搜索

介绍

上一篇教程 中,我们讨论了如何使用 pskillnice 命令来控制系统上的进程。 本指南重点介绍 bash、Linux 系统和您的终端如何结合在一起提供流程和作业控制。

本文将着重于管理前台和后台进程,并将演示如何利用 shell 的作业控制功能来获得更多运行命令的灵活性。

先决条件

要遵循本指南,您需要访问运行 bash shell 界面的计算机。 bash 是许多基于 Linux 的操作系统的默认 shell,它可用于许多类 Unix 操作系统,包括 macOS。 请注意,本教程使用运行 Ubuntu 20.04 的 Linux 虚拟专用服务器进行了验证。

如果您打算使用远程服务器来遵循本指南,我们建议您先完成我们的 初始服务器设置指南 。 这样做将为您设置一个安全的服务器环境——包括一个具有 sudo 权限的非 root 用户和一个配置了 UFW 的防火墙——您可以使用它来培养您的 Linux 技能。

或者,您可以使用此页面上嵌入的交互式终端来试验本教程中的示例命令。 单击以下 Launch an Interactive Terminal! 按钮以打开终端窗口并开始使用 Linux (Ubuntu) 环境。

启动交互式终端!

管理前台进程

您在 Linux 机器上启动的大多数进程都将在前台运行。 该命令将开始执行,在整个过程中阻止使用 shell。 该过程可能允许用户交互或可能只是运行一个过程然后退出。 默认情况下,任何输出都将显示在终端窗口中。 我们将在以下小节中讨论管理前台进程的基本方法。

启动进程

默认情况下,进程在前台启动。 这意味着在程序退出或更改状态之前,您将无法与 shell 交互。

一些前台命令很快退出并几乎立即返回到 shell 提示符。 例如,以下命令将 Hello World 打印到终端,然后返回到命令提示符:

echo "Hello World"
OutputHello World

其他前台命令需要更长的时间才能执行,从而在其持续时间内阻止 shell 访问。 这可能是因为该命令正在执行更广泛的操作,或者因为它被配置为运行直到它被显式停止或直到它接收到其他用户输入。

无限期运行的命令是 top 实用程序。 启动后,它将继续运行并更新其显示,直到用户终止进程:

top

您可以通过按q退出top,但其他一些进程没有专门的退出功能。 要阻止这些,您必须使用另一种方法。

终止进程

假设您在命令行上启动了一个简单的 bash 循环。 例如,以下命令将启动一个循环,每十秒打印一次 Hello World。 这个循环将永远持续下去,直到明确终止:

while true; do echo "Hello World"; sleep 10; done

top 不同,这样的循环没有“退出”键。 您必须通过向其发送 信号 来停止该过程。 在 Linux 中,内核可以向正在运行的进程发送信号,作为它们退出或更改状态的请求。 Linux 终端通常配置为在用户按下 CTRL + C 组合键时向当前前台进程发送“SIGINT”信号(“信号中断”的缩写)。 SIGINT 信号告诉程序用户已使用键盘请求终止。

要停止已开始的循环,请按住 CTRL 键并按 C 键:

CTRL + C

循环将退出,将控制权返回给 shell。

CTRL + C 组合发送的 SIGINT 信号是可以发送给程序的众多信号之一。 大多数信号没有与之关联的键盘组合,而是必须使用 kill 命令发送,本指南稍后将对此进行介绍。

暂停进程

如前所述,前台进程将在执行期间阻止对 shell 的访问。 如果您在前台启动一个进程,但随后意识到您需要访问终端怎么办?

您可以发送的另一个信号是“SIGTSTP”信号。 SIGTSTP 是“信号终端停止”的缩写,通常表示为 20 号信号。 当您按下 CTRL + Z 时,您的终端会注册一个“挂起”命令,然后将 SIGTSTP 信号发送到前台进程。 本质上,这将暂停命令的执行并将控制权返回给终端。

为了说明,每 5 秒使用 ping 连接到 google.com。 以下命令在带有 commandping 命令之前,这将允许您绕过人为设置命令最大计数的任何 shell 别名:

command ping -i 5 google.com

不要使用 CTRL + C 终止命令,而是按 CTRL + Z。 这样做将返回如下输出:

Output[1]+  Stopped                 ping -i 5 google.com

ping 命令已暂时停止,让您再次访问 shell 提示符。 您可以使用 ps 处理工具来显示:

ps T
Output  PID TTY      STAT   TIME COMMAND
26904 pts/3    Ss     0:00 /bin/bash
29633 pts/3    T      0:00 ping -i 5 google.com
29643 pts/3    R+     0:00 ps t

此输出表明 ping 进程仍在列出,但“STAT”列中有一个“T”。 根据 ps 手册页,这意味着作业已“被 [a] 作业控制信号停止”。

本指南将概述如何更深入地更改进程状态,但现在您可以通过键入以下内容再次在前台恢复命令的执行:

fg

进程恢复后,使用 CTRL + C 终止它:

管理后台进程

在前台运行进程的主要替代方法是允许它在后台执行。 后台进程与启动它的特定终端相关联,但不会阻止对 shell 的访问。 相反,它在后台执行,使用户能够在命令运行时与系统交互。

由于前台进程与其终端交互的方式,每个终端窗口只能有一个前台进程。 由于后台进程无需等待进程完成就立即将控制权返回给 shell,因此许多后台进程可以同时运行。

启动进程

您可以通过在命令末尾附加一个 & 字符 (&) 来启动后台进程。 这告诉外壳程序不要等待进程完成,而是开始执行并立即将用户返回到提示符。 命令的输出仍将显示在终端中(除非 redirected),但您可以在后台进程继续时键入其他命令。

例如,您可以通过键入以下内容在后台启动上一节中相同的 ping 进程:

command ping -i 5 google.com &

bash 作业控制系统将返回如下输出:

Output[1] 4287

然后,您将收到来自 ping 命令的正常输出:

OutputPING google.com (74.125.226.71) 56(84) bytes of data.
64 bytes from lga15s44-in-f7.1e100.net (74.125.226.71): icmp_seq=1 ttl=55 time=12.3 ms
64 bytes from lga15s44-in-f7.1e100.net (74.125.226.71): icmp_seq=2 ttl=55 time=11.1 ms
64 bytes from lga15s44-in-f7.1e100.net (74.125.226.71): icmp_seq=3 ttl=55 time=9.98 ms

但是,您也可以同时键入命令。 后台进程的输出将混合在前台进程的输入和输出中,但不会干扰前台进程的执行。

列出后台进程

要列出所有停止或后台进程,可以使用 jobs 命令:

jobs

如果您仍然在后台运行之前的 ping 命令,则 jobs 命令的输出将类似于以下内容:

Output[1]+  Running                 command ping -i 5 google.com &

这表明您当前有一个后台进程正在运行。 [1] 代表命令的 作业规格 或作业编号。 您可以通过在作业编号前加上百分号来引用其他作业和流程控制命令,例如 killfgbg。 在这种情况下,您会将此作业称为 %1

停止后台进程

您可以通过几种方式停止当前的后台进程。 最直接的方法是将 kill 命令与关联的作业编号一起使用。 例如,您可以通过键入以下内容来终止正在运行的后台进程:

kill %1

根据终端的配置方式,立即或下次点击 ENTER 时,作业终止状态将出现在输出中:

Output[1]+  Terminated              command ping -i 5 google.com

如果您再次检查 jobs 命令,将不会有任何当前作业。

改变进程状态

现在您知道如何在后台启动和停止进程,您可以了解如何更改它们的状态。

本指南已经概述了更改进程状态的一种方法:使用 CTRL + Z 停止或暂停进程。 当进程处于停止状态时,您可以将前台进程移至后台,反之亦然。

将前台进程移至后台

如果您在启动命令时忘记用 & 结束命令,您仍然可以将进程移至后台。

第一步是再次使用 CTRL + Z 停止该过程。 一旦进程停止,您可以使用 bg 命令在后台再次启动它:

bg

您将再次收到作业状态行,这次附加了 & 符号:

Output[1]+ ping -i 5 google.com &

默认情况下,bg 命令在最近停止的进程上运行。 如果您连续停止了多个进程而没有再次启动它们,则可以通过其作业编号引用特定进程,以将正确的进程移至后台。

请注意,并非所有命令都可以在后台运行。 如果某些进程检测到它们的标准输入和输出直接连接到活动终端,它们将自动终止。

将后台进程移至前台

您还可以通过键入 fg 将后台进程移动到前台:

fg

这将在您最近的后台进程上运行(由 jobs 命令输出中的 + 指示)。 它立即暂停进程并将其置于前台。 要指定不同的作业,请使用其作业编号:

fg %2

一旦作业在前台,您可以使用 CTRL + C 将其终止,让它完成,或暂停并再次将其移至后台。

处理 SIGHUP

无论进程是在后台还是前台,它都与启动它的终端实例紧密相关。 当终端关闭时,它通常会向绑定到终端的所有进程(前台、后台或已停止)发送一个 SIGHUP 信号。 这表示进程终止,因为它们的控制终端很快将不可用。

但是,有时您可能想关闭终端但保持后台进程运行。 有多种方法可以实现这一点。 一种更灵活的方法是使用终端多路复用器,如 screentmux。 另一种解决方案是使用提供 screentmux 的分离功能的实用程序,例如 dtach

但是,这并不总是一种选择。 有时这些程序不可用,或者您已经开始需要继续运行的进程。 有时,对于您需要完成的事情,这些甚至可能是矫枉过正。

使用 nohup

如果您在启动进程时知道要在进程完成之前关闭终端,您可以使用 nohup 命令启动它。 这使得启动的进程不受 SIGHUP 信号的影响。 当终端关闭时它将继续运行,并将被重新分配为 init 系统的子系统:

nohup ping -i 5 google.com &

这将返回如下所示的行,表示命令的输出将被写入名为 nohup.out 的文件中:

Outputnohup: ignoring input and appending output to ‘nohup.out’

如果可写,此文件将放置在您当前的工作目录中,否则将放置在您的主目录中。 这是为了确保在终端窗口关闭时不会丢失输出。

如果您关闭终端窗口并打开另一个窗口,该进程仍将运行。 您不会在 jobs 命令的输出中找到它,因为每个终端实例都维护自己独立的作业队列。 关闭终端会导致 ping job 被销毁,即使 ping process 仍在运行。

要杀死 ping 进程,您必须找到它的进程 ID(或“PID”)。 您可以使用 pgrep 命令来做到这一点(还有一个 pkill 命令,但是这个由两部分组成的方法可确保您只杀死预期的进程)。 使用 pgrep-a 标志搜索可执行文件:

pgrep -a ping
Output7360 ping -i 5 google.com

然后,您可以通过引用返回的 PID(即第一列中的数字)来终止该进程:

kill 7360

如果您不再需要 nohup.out 文件,您可能希望删除它。

使用 disown

nohup 命令很有帮助,但前提是您知道在开始该过程时将需要它。 bash 作业控制系统通过内置的 disown 命令提供了其他实现类似结果的方法。

disown 命令在其默认配置中从终端的作业队列中删除作业。 这意味着它不能再使用本指南前面讨论的作业控制机制进行管理,例如 fgbgCTRL + ZCTRL + C。 相反,该作业将立即从 jobs 输出的列表中删除,并且不再与终端关联。

通过指定作业号调用该命令。 例如,要立即拒绝工作 2,您可以键入:

disown %2

这使得进程在控制终端关闭后处于与 nohup 进程相同的状态。 例外情况是,如果控制终端没有被重定向到文件,则在关闭控制终端时任何输出都将丢失。

通常,如果您不立即关闭终端窗口,您不希望将进程完全从作业控制中删除。 您可以将 -h 标志传递给 disown 进程,以将进程标记为忽略 SIGHUP 信号,但以其他方式继续作为常规作业:

disown -h %1

在这种状态下,您可以使用正常的作业控制机制来继续控制进程,直到关闭终端。 关闭终端后,如果您在启动时没有重定向到文件,您将再次陷入无处可输出的进程。

要解决此问题,您可以尝试在进程已经运行后重定向它的输出。 这超出了本指南的范围,但 这篇文章 提供了如何做到这一点的解释。

使用 huponexit 外壳选项

bash 有另一种方法可以避免子进程的 SIGHUP 问题。 huponexit shell 选项控制 bash 在退出时是否会向其子进程发送 SIGHUP 信号。

注意huponexit 选项仅在从 shell 本身 启动 shell 会话终止 时影响 SIGHUP 行为。 这适用的一些示例是在会话中按下 exit 命令或 CTRL + D 时。

当通过终端程序本身(通过关闭窗口等)结束 shell 会话时,命令 huponexit 将具有 no 影响。 代替 bash 决定是否发送 SIGHUP 信号,终端本身会将 SIGHUP 信号发送到 bash,然后它将正确地将信号传播到其子进程。


尽管有上述警告,huponexit 选项可能是最容易管理的选项之一。 您可以通过键入以下内容来确定此功能是打开还是关闭:

shopt huponexit

要打开它,请键入:

shopt -s huponexit

现在,如果您通过键入 exit 退出会话,您的进程将继续运行:

exit

这与最后一个选项对程序输出有相同的警告,因此如果这很重要,请确保在关闭终端之前重定向进程的输出。

结论

学习作业控制以及如何管理前台和后台进程将为您在命令行上运行程序提供更大的灵活性。 不必打开许多终端窗口或 SSH 会话,您通常可以提前停止进程或根据需要将它们移至后台。