7 作业控制
本章讨论什么是作业控制,如何工作,和Bash提供了何种工具以控制作业。
作业控制基础: 作业控制是如何工作的。
作业控制内建命令: 与作业控制交互的Bash内建命令。
作业控制变量: Bash用来定制化作业控制的变量。
7.1 作业控制基础
作业控制是指一种能力:有选择地停止(挂起)进程的执行,和在之后某个点继续(恢复)它们的执行。用户典型地使用这种工具的方式,是通过操作系统内核的终端驱动器和Bash联合提供的交互接口。
shell连接一个作业和一个管道。shell保持一个表格——其内容是当前正在执行的作业,可以使用命令jobs列出。当Bash异步启动一个作业时,它打印这样一行:
[1] 25647
为了便于作业控制的用户接口的实现,操作系统维护一个当前终端进程组ID的概念。这个进程组(进程组ID等于当前终端进程组ID的那些进程)的成员,接收键盘产生的信号,比如SIGINT。这些进程被称为前台进程。后台进程是那些进程组ID和终端进程组ID不同的进程,这些进程无视键盘产生的信号。仅仅前台进程允许从终端读——如果用户指定了 stty tostop,则可以向终端写。后台进程试图从终端读时,(当 stty tostop被设定有效时,向终端写),由内核的终端驱动器发给它一个 SIGTTIN (SIGTTOU) 信号,除非该信号被捕获,否则挂起该后台进程。
如果运行Bash的操作系统支持作业控制,则Bash包含使用它的工具。在一个进程正在运行时,键入 suspend 字符(典型地 ‘^Z’, Control-Z),会导致该进程被停止,并返回控制到Bash。键入 delayed suspend 字符(典型地 ‘^Y’, Control-Y),导致该进程在它试图从终端读取时停止,并返回控制到Bash。用户可以控制该作业的状态,使用命令 bg 使它继续在后台运行, 命令 fg 使它继续在前台运行,或命令kill 杀掉该进程。 ‘^Z’ 立即生效,并且有这样的副作用:引起丢弃后续的输出和先前的输入。
shell中有很多方法参照一个作业。字符 ‘%’ 引出一个作业描述(jobspec)。
作业号n可以用 ‘%n’ 来参照。 符号 ‘%%’ 和 ‘%+’ 参照shell的当前作业,该作业是上一个在前台停止的作业,或者在后台启动的作业。单一的 ‘%’ (不带作业描述)也指向当前的作业。前一个作业可以用 ‘%-’ 来参照。如果只有一个作业,则 ‘%+’ 和 ‘%-’ 都可以被用来指向同一个作业。在输出,与作业相关的(例如,命令jobs的输出),当前作业总是用 ‘+’ 标记,而前一个作业带 ‘-’ 标记。
一个作业也可以使用启动它的名字的前缀,或者在该命令行中的一个子字符串来参照。例如:‘%ce’ =指向一个停止的 ce 作业。 另一方面,使用 ‘%?ce’ 指向任何命令行包含字符串 ‘ce’ 的作业。如果前缀或者子字符串匹配多于一个作业,则Bash报告错误。
简单地指定一个作业的名字,可以把它带到前台: ‘%1’ 与 ‘fg %1’ 同义,把作业1从后台带到前台。类似的,‘%1 &’ 继续 作业1 在后台的执行,相当于 ‘bg %1’ 。
shell在一个作业改变了状态时,立刻就知道了。一般地,Bash直到在打印一个提示符前,才报告一个作业的状态变化,以不中断其他的输出。 如果内建命令 set 的 -b 选项启用了,Bash 立即报告该种改变(见 内建命令set 章节)。每个子进程退出时,都执行 SIGHLD 的陷阱。
当作业被停止时,打算退出Bash,(或者在作业运行时,如果 checkjobs 选项被启用 —— 见 内建命令 shopt 章节),shell打印一条警告信息,如果 checkjobs 选项启用了,则列出作业及其状态。命令jobs用来检测它们的状态。如果再次企图退出shell,并且中间没有运行其他命令,则Bash不再打印其他警告,并且所有停止的作业都被终止。
7.2 作业控制内建命令
bg
bg [jobspec …]
恢复后台里被挂起的每一个作业jobspec的运行,和使用 ‘&’ 启动作业的效果一样。如果没有指定 jobspec ,则操作的是当前作业。返回状态是0,除非未启用作业控制时运行它,或者在作业控制启用情况下运行,找不到任何 jobspec ,或者指定了一个没有以作业控制启动的作业。
fg
fg [jobspec]
恢复前台里的作业 jobspec, 使得该作业成为当前作业。如果 jobspec 未指定,则使用当前的作业。返回状态是命令放置到前台,或者非零——如果当作业控制被取消,或者作业控制启用时运行, jobspec 没有指定到一个有效的作业,或者 jobspec 指定了一个没有以作业控制启动的作业。
jobs
jobs [-lnprs] [jobspec]
jobs -x command [arguments]
第一个格式,列出活动的作业。选项具有如下含义:
-l
在一般信息之外,列出进程ID。
-n
仅仅显示自最近一次用户被通知作业的状态之后,改变了状态的作业。
-p
仅仅列出作业的进程组头部的进程ID。
-r
限制仅仅显示正在运行的作业。
-s
限制仅仅显示已经停止的作业。
如果给定了 jobspec ,输出信息仅限于相关作业。如果未提供 jobspec, 则列出所有作业的状态。
如果提供了-x 选项, jobs 把在 command 或者arguments 中找到的 jobspec 替换为相应的进程组ID,和执行的 command ,传给它的 arguments,返回它的退出状态。
kill
kill [-s sigspec] [-n signum] [-sigspec] jobspec or pid
kill -l [exit_status]
发送由 sigspec 或者 signum 指定的信号给由作业说明 jobspec 或者进程ID pid 对应的进程。 sigspec可以是一个大小写不敏感的信号名字,比如 SIGINT(可以带或不带SIG前缀),或者一个信号数字,signum 是一个信号数字。如果 sigspec 和 signum 都没有给定,则使用 SIGTERM。 -l 选项列出信号名字。如果当使用 -l 选项时,提供了参数, 则列出参数相应的信号名字,并且返回状态是0。 exit_status 是一个数字,指定了一个信号数字,或者由一个信号终止的进程的退出状态。如果至少有一个信号被成功发送了,则返回状态是0,如果发生错误,或者有无效选项,则返回非0。
wait
wait [jobspec or pid ...]
等待,直到由每一个进程ID: pid 或者作业说明 jobspec 指定的子进程退出,并且返回状态是等待的最后的命令的退出状态。如果给定了作业说明,要等待该作业中的所有进程。如果没有给定参数,等待所有当前活动的子进程,并且返回状态是0。如果 jobspec 和 pid 都没有指定shell 的活动子进程,返回状态127。
disown
disown [-ar] [-h] [jobspec …]
没有选项时,从活动作业表中移除每个 jobspec 。如果给了 -h 选项,则该作业不从表中移除,但是做一个标记,以使如果shell 收到 SIGHUP 时,不发送 SIGHUP 到该作业。如果没有指定 jobspec ,而且也没有给定 -a 或者 -r 选项,则使用当前作业。如果没有指定 jobspec ,则 -a 选项意味着移除或者标记所有作业;不带 jobspec 参数的 -r 选项限制操作仅仅针对正在运行的作业。
suspend
suspend [-f]
挂起当前的shell的执行,直到收到一个 SIGCONT 信号。登录shell不能被挂起;-f 选项可以用来覆盖此行为,并强制挂起。
当作业控制没有激活时,内建命令 kill 和 wait 不接受 jobspec 参数。必须提供进程ID作为参数。
7.3 作业控制变量
auto_resume
这个变量控制shell如何和用户和作业控制交互。如果此变量存在,则不带重定向的单个单词简单命令,会被当做一个存在的作业再继续的候选。不允许意思含糊;如果以该字符串开头的作业多于1个,则选择最近访问的作业。已经停止的作业的名字,在这个上下文中,是用来启动该作业的命令行。如果此变量被设置为“exact” ,则提供的字符串必须完全匹配一个已经停止的作业的名字;如果此变量被设置为 “substring” ,则提供的字符串必须匹配一个已经停止的作业的名字的子字符串。 “substring” 这个值提供了和 ‘%’ 作业ID(见 作业控制基础 章节)相同的作用。