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

介绍

在以前的教程中(LINK0),我们讨论了如何使用ps,killnice命令来控制系统上的流程,本指南强调了如何将bash,Linux系统和终端结合起来,以提供流程和工作控制。

本文将专注于管理前台和后台流程,并展示如何利用壳的工作控制功能,以便在执行命令方面获得更多的灵活性。

前提条件

要跟随本指南,您需要访问运行bash壳接口的计算机。bash是许多基于Linux的操作系统上的默认壳,并且可以在包括macOS在内的许多类似Unix的操作系统上使用。

如果您打算使用远程服务器来遵循本指南,我们建议您先完成我们的 初始服务器设置指南

管理前沿过程

您在 Linux 机器上启动的大多数流程将在前台运行. 该命令将启动执行,阻止在流程的持续时间内使用壳。 该流程可能允许用户交互,或者可能只是通过程序运行,然后退出。

开始一个过程

默认情况下,流程在前面启动,这意味着直到程序退出或状态更改,您将无法与壳进行交互。

例如,下列命令将打印Hello World到终端,然后返回您的命令提示:

1echo "Hello World"
1[secondary_label Output]
2Hello World

其他前台命令需要更长的时间来执行,这会阻止壳存取时间,这可能是因为命令正在执行更广泛的操作,或者是因为命令已配置为运行,直到明确停止或接收其他用户输入。

一个无限期运行的命令是顶部实用程序. 启动后,它将继续运行并更新其显示,直到用户终止该过程:

1top

您可以通过按q来停止顶部,但有些其他流程没有专门的停止功能。

终止一个过程

假设你在命令行上启动一个简单的bash循环,例如,下面的命令会启动一个循环,每10秒打印一次Hello World

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

在 Linux 中,内核可以向运行过程发送信号,以要求他们退出或改变状态。Linux 终端通常配置为将SIGINT信号(简称信号中断)发送到当前的前沿过程,当用户按下CTRL + C键组合。

要停止您已启动的循环,请保持CTRL键,然后按C键:

1CTRL + C

循环将退出,将控制返回壳。

通过CTRL + C组合发送的SIGINT信号是可以发送到程序的许多信号之一. 大多数信号没有与它们相关的键盘组合,而必须使用杀死命令发送,这将在本指南晚些时候讨论。

暂停程序

如前所述,前台进程将在执行期间阻止对壳的访问. 如果你在前台启动一个进程,但然后意识到你需要访问终端呢?

您可以发送的另一个信号是SIGTSTP信号。SIGTSTP是信号终端停止的缩写,通常被表示为信号号号 20. 当您按CTRL + Z时,您的终端会记录暂停命令,然后将SIGTSTP信号发送到前台进程中。

为了说明,使用ping连接到google.com每5秒钟。下列命令先于ping命令command,这将允许您绕过任何人工设置命令最大计数的壳名称:

1command ping -i 5 google.com

不要用「CTRL + C」终止命令,而是按「CTRL + Z」。

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

ping 命令已暂时停止,您可以再次访问 shell 提示,您可以使用 ps 流程工具来显示:

1ps T
1[secondary_label Output]
2  PID TTY STAT TIME COMMAND
326904 pts/3 Ss 0:00 /bin/bash
429633 pts/3 T 0:00 ping -i 5 google.com
529643 pts/3 R+     0:00 ps t

此输出表示ping过程仍在列表中,但STAT列表中包含一个T

本指南将概述如何更深入地更改进程状态,但目前您可以通过键入重新恢复在前台执行命令:

1fg

一旦该过程恢复,请用CTRL + C结束:

管理背景流程

在前台运行过程的主要替代方案是允许它在后台执行. 背景过程与启动该过程的特定终端相关联,但不会阻止对壳的访问。

由于前端过程与其终端相互作用的方式,每个终端窗口只能有一个前端过程,因为后端过程可以立即返回对壳的控制,而不需要等待过程完成,所以许多后端过程可以同时运行。

开始进程

您可以通过将 ampersand 字符(&)附加到您的命令的末尾来启动背景流程,这意味着壳不应该等待过程完成,而是要开始执行,并立即将用户返回提示。

例如,您可以从背景中上一节开始同样的ping过程,键入:

1command ping -i 5 google.com &

bash工作控制系统会像这样返回输出:

1[secondary_label Output]
2[1] 4287

然后您将收到从ping命令的正常输出:

1[secondary_label Output]
2PING google.com (74.125.226.71) 56(84) bytes of data.
364 bytes from lga15s44-in-f7.1e100.net (74.125.226.71): icmp_seq=1 ttl=55 time=12.3 ms
464 bytes from lga15s44-in-f7.1e100.net (74.125.226.71): icmp_seq=2 ttl=55 time=11.1 ms
564 bytes from lga15s44-in-f7.1e100.net (74.125.226.71): icmp_seq=3 ttl=55 time=9.98 ms

但是,您也可以同时键入命令. 背景流程的输出将混合到您的前台流程的输入和输出之间,但不会干扰前台流程的执行。

背景流程列表

要列出所有停止或背景的进程,您可以使用工作命令:

1jobs

如果您仍然在背景中运行以前的ping命令,则jobs命令的输出将类似于以下:

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

这表明您目前有一个单一的背景流程正在运行. [1] 表示命令的 job spec 或 job 号码. 您可以通过以百分比符号提前工作号码来参考此命令,例如 killfg 和 `bg 等其他任务和过程控制命令。

停止背景流程

您可以通过几种方式阻止当前的背景流程。最简单的方法是使用杀死命令与相关的任务号码。

1kill %1

取决于您的终端是如何配置的,即时或下次点击ENTER,工作终止状态将出现在输出中:

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

如果您再次检查工作命令,则不会有当前的工作。

变革过程国家

现在你知道如何在背景中启动和停止流程,你可以学习如何改变他们的状态。

本指南已经概述了一种方法来改变一个过程的状态:用CTRL + Z停止或暂停一个过程。

将前沿过程移动到背景

如果您在启动命令时忘记用&结束命令,您仍然可以将过程移动到背景。

第一步是用CTRL + Z再次停止进程.一旦进程停止,您可以使用bg命令在背景中重新启动进程:

1bg

您将再次收到工作状态行,这一次附带的 ampersand:

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

默认情况下,bg命令在最近停止的进程上运行. 如果您连续停止了多个进程而没有重新启动它们,则可以将特定进程引用其任务号码,以将正确的进程移动到背景。

请注意,并非所有命令都可以背景化,但有些过程会自动终止,如果它们检测到已启动的标准输入和输出直接连接到一个活跃的终端。

将背景过程移动到前面

您也可以通过键入fg将背景过程移动到前台:

1fg

它运行在你最近的背景过程(在工作命令的输出中标记为+)中,它立即暂停了过程,并将其置于前沿。

1fg %2

一旦工作处于前沿,您可以使用CTRL + C来杀死它,让它完成,或者暂停并将其重新移动到后台。

与SIGHUPS打交道

无论是一个过程是在后台还是在前台,它都与启动该过程的终端实例紧密联系在一起. 当一个终端关闭时,它通常会向与终端相关的所有进程(前台、后台或停止)发送SIGHUP信号。

然而,可能会有时候你想关闭终端,但要保持背景流程的运行。有几种方法可以做到这一点。更灵活的方法之一是使用一个终端倍增器,如 screentmux

然而,这并不总是一个选项,有时这些程序是不可能的,或者你已经开始了你需要继续运行的过程,有时这些甚至可能是你需要完成的事情的过度杀戮。

使用nohup

如果你知道在开始过程时,你想在完成过程之前关闭终端,你可以使用nohup命令开始它,这使得开始的过程免受SIGHUP信号的影响。

1nohup ping -i 5 google.com &

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

1[secondary_label Output]
2nohup: ignoring input and appending output to ‘nohup.out’

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

如果您关闭终端窗口并打开另一个终端窗口,则该过程仍在运行. 您不会在jobs命令的输出中找到它,因为每个终端实例都有自己的独立的任务队列。

要杀死ping过程,你必须找到它的进程ID(或PID)你可以用pgrep命令(也有pkill命令,但这两部分的方法确保你只杀死预期的进程)。

1pgrep -a ping
1[secondary_label Output]
27360 ping -i 5 google.com

然后,您可以通过引用返回的 PID 来杀死该过程,这是第一个列中的数字:

1kill 7360

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

使用解除

nohup命令是有用的,但只有当你知道你在启动过程时需要它时,bash任务控制系统提供了其他方法来实现类似的结果,使用内置的disown命令。

取消命令,在其默认配置中,将任务从终端的任务排列中删除,这意味着它不再可以使用本指南之前讨论的任务控制机制进行管理,如fg,bg,CTRL + Z,CTRL + C

命令是通过指定工作号码来调用,例如,要立即放弃工作2,您可以键入:

1disown %2

这使得控制终端关闭后过程处于一个状态,而不是一个nohup过程的状态,例外是当控制终端关闭时,如果它没有被重定向到文件时,任何输出都会丢失。

通常情况下,如果您不立即关闭终端窗口,您不希望完全从任务控制中删除该过程,您可以将h标志转移到被取消进程,以便标记该进程以忽略 SIGHUP 信号,但否则可以继续作为常规工作:

1disown -h %1

在这种状态下,您可以使用正常的工作控制机制来继续控制过程,直到关闭终端. 关闭终端后,您将再次被困在一个进程中,如果在启动时没有重定向到文件。

要围绕此工作,您可以尝试在运行后重定向进程的输出,但这不在本指南的范围内,但 此帖子提供了您如何做到这一点的解释。

使用huponexit壳选项

bash有另一种方法来避免儿童过程中的SIGHUP问题. huponexit壳选项控制bash是否会发送其孩子处理SIGHUP信号,当它退出。

<$>[注] **注:huponexit选项只会影响SIGHUP行为,当从壳本身启动壳会话终止时 **。

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

尽管有上述警告,但huponexit选项肯定是最容易管理的选项之一,您可以通过键入来确定此功能是否已启用或关闭:

1shopt huponexit

打开它,类型:

1shopt -s huponexit

现在,如果您通过键入退出来退出您的会话,您的所有流程将继续运行:

1exit

它具有与最后一个选项相同的程序输出警告,所以请确保在关闭终端之前重定向您的流程输出,如果这很重要。

结论

学习工作控制以及如何管理前台和后台流程将为您提供更大的灵活性,而不是需要打开许多终端窗口或SSH会话,您通常可以提前停止流程或根据需要将其移动到后台。

Published At
Categories with 技术
comments powered by Disqus