作业控制

作业控制是BSD在1980年前后增加的一个特性。
它允许在一个终端上启动多个作业(进程组),它支持控制哪一个作业可以访问终端,以及哪些作业可以在后台运行。

作业可以看做是shell管理的进程

作业控制的条件

主要有三点:

  1. 支持作业控制的shell
  2. 内核中的终端驱动程序必须支持作业控制
  3. 内核必须提供对某些作业控制信号的支持

理解作业控制

从shell使用作业控制功能角度讲,用户可以在前台或后台启动一个作业。
如以下命令:

$ vim main.c

就会在前台启动只有一个进程组成的作用。

$ pr *c | lpr &
$ make all &

在后台启动了两个作业,这两个作业调用的所有进程都在后台运行。

当启动一个后台作业时,shell赋予它一个作业标识,并打印一个或几个进程的ID。

$ make all > Make.out &
[1]  1475
$ pr *c | lpr &
[2]  1490
$ 				(键入回车)
[2] + Done		pr *c | lpr &
[1] + Done		make all > Make.out &

make的作业号是1,启动的进程ID是1475.下一个管道线是作业号2,其第一个进程的进程ID是1490。
当我们作业完成并且键入回车时,shell通知我们作业已完成并打印其提示符。

shell并不在任何随意时刻打印后台作业的状态改变,它只在打印其提示符让用户输入新的命令行之前才这样做。

改变作业控制

我们可以依靠挂起键Ctrl+Z来影响前台作业
键入此字符使终端驱动程序将信号SIGTSTP送至前台进程组中的所有进程后台作业则不受影响。

以下三个特殊字符可使终端驱动程序产生信号,并将其送至前台

  • 中断字符DELETE或Ctrl+C)产生SIGINT
  • 退出字符Ctrl+\)产生SIGQUIT
  • 挂起字符Ctrl+Z)产生SIGTSTP

当我们有一个前台作业以及或若干个后台作业时,是哪一个接受我们在终端上键入的字符呢?

只有前台作业接收终端输入!
但是!后台作业试图读取终端也不是一个错误,终端驱动程序将会检测到这种情况,并且向后台作业试图读终端发送一个信号SIGTTIN,该信号会暂停此后台作业。

后台作业如何读取终端输入

当然,用户可以用shell命令将此作业转为前台作业运行。

$ car > temp.foo &		//在后台启动,但是从标准输入读
[1]  1681
$ 		(Enter)
[1] + Stopped(SIGTTIN)	//cat > temp.foo &
$ fd %1					//1号作业成为前台作业
cat > temp.foo
hello world				//输入hello world
^D						//输入文件结束符
$ cat temp.foo
hello world

后台作业如何输出到终端

后台作业输出到终端是一个我们可以允许或禁止的选项,通常用stty命令。

$ cat temp.foo &
[1]  1719
$ hello world
			(Enter)
[1] + Done
$ stty tostop		//禁止后台作业输出至控制终端
$ cat temp.foo &
[1]  1721
$			(Enter)
[1] + Stopped(SIGTTOU)		cat temp.foo &
$ fg %1
cat temp.foo
hello world

参考文献

[1] UNIX环境高级编程(第二版)