任务控制
任务控制这个功能允许我们从单独的终端里开始多个任务(进程组)并去控制这些任务访问终端并在后台返回任务的结果。任务控制需要三种支持形式:
- 一个支持任务控制的shell
- 终端驱动必须支持任务控制
- 内核必须支持某些任务控制的信号
SHELL并不会打印出后台任务状态改变的信息,只有在需要输入新的命令行时,才会打印出提示。
在和终端驱动交互时,一些特殊字符会影响前台任务:如挂起键(ctrl+z)。这个字符会让终端驱动发送SIGTSTP信号到前台进程组中所有的进程。所有后台进程组中的任务不会受到影响。终端驱动可以识别出三种特殊字符,它产生的信号会发送到前台进程组中。
- 中断字符(一般是DELETE或ctrl+c)产生SIGINT。
- 退出字符(一般是CTRL+\)产生SIGQUIT。
- 挂起字符(一般是CTRL+Z)产生SIGTSTP。
只要能被终端驱动处理,也可以产生其它的控制条件。只有前台任务可以收到终端输入。当终端驱动发现后台任务尝试从终端读取时,它会发送一个特殊信号给后台任务:SIGTTIN。这个信号一般是停止后台任务;通过使用SHELL,我们会得到一个通知,并能让该任务回到前台,如:
$ cat > temp.foo & start in background, but it'll read from standard input
[1] 1681
$ we press RETURN
[1] + Stopped (SIGTTIN) cat > temp.foo &
$ fg %1 bring job number 1 into the foreground
cat > temp.foo the shell tells us which job is now in the foreground
hello, world enter one line
^D type the end-of-file character
$ cat temp.foo check that the one line was put into the file
hello, world
|
shell在后台开始cat进程,但是当cat尝试读取它的标准输入(当前控制终端)时,终端驱动知道它是一个后台任务,发送SIGTTIN信号到后台任务。shell发现子进程状态改变,并告诉我们这个任务已经停止。之后,我们用fg命令把停止的任务移动到前台。这么做使得shell把任务放到前台进程组(tcsetpgrp),并发送继续信号(SIGCONT)到进程组。因为该任务已经在前台进程组中,所以它能从控制终端中读取。
后台任务发送数据到控制终端时会发生什么?这是个选项,我们可以允许也可以禁止。一般我们用stty命令改变这个选项。下面显示了它是如何工作的:
$ cat temp.foo & execute in background
[1] 1719
$ hello, world the output from the background job appears after the prompt
we press RETURN
[1] + Done cat temp.foo &
$ stty tostop disable ability of background jobs to output to
controlling terminal
$ cat temp.foo & try it again in the background
[1] 1721
$ we press RETURN and find the job is stopped
[1] + Stopped(SIGTTOU) cat temp.foo &
$ fg %1 resume stopped job in the foreground
cat temp.foo the shell tells us which job is now in the foreground
hello, world and here is its output
|
当我们禁止后台任务向控制终端输出时,cat将会阻塞它输出到标准输出,因为终端驱动标识了这个动作来自后台进程,并向任务发送SIGTTOU信号。就像之前的例子一样,使用fg命令把任务带到前台,之后任务完成所有任务。
图9.8汇总了任务控制的一些功能。在终端驱动块中的实线意味着终端I/O(terminal I/O)和终端产生的信号(terminal-generated signals)一直从着前台进程组连接到实际终端。而虚线连接的SIGTTOU信号意味着一个后台进程输到终端的输出是可选的。