Linux作业控制

作业控制的概念

交互式shell的基本目的是从用户终端读取命令,并创建执行这些命令指定的程序的进程。 单个命令可能只运行一个进程,但是一个命令通常会使用多个进程。如果在shell命令中使用“ |”运算符,则会在自己的进程中显式请求几个进程。但是,即使只运行一个程序,它也可以在内部使用多个进程。例如,单个编译命令(例如:“cc -c foo.c”)通常使用四个进程。如果运行make则其工作是在单独的进程中运行其他程序。

属于单个命令的进程被称为进程组或作业(job),这样一来,就可以一次对所有进程进行操作。例如,输入Control-C将发送SIGINT终止前台进程组中的所有进程。

会话(session)是更大的一组进程。通常,一次登录产生的所有进程都属于同一个会话。

每个进程都属于一个进程组。当一个进程创建了,它成为与其父进程相同的进程组和会话(session)的成员。可以通过setsid函数将其放在另一个进程组中,前提是该进程组属于同一会话。

通常,新会话是由系统登录程序创建的,会话leader(session leader)是运行用户登录shell的进程。

支持作业控制的shell必须安排可以随时使用该终端的作业。否则可能有多个作业试图一次从终端读取, 并且对于哪个进程应该接收用户的输入感动困惑。为了避免这种情况,shell必须使用一种协议与终端驱动配合使用。

shell一次只能向一个进程提供控制终端无限制的访问。这称为控制终端的前台作业(foreground job)。在没有对终端访问权限的情况下执行的,由shell程序管理的其他进程组称为后台作业(background jobs)。

如果一个后台作业需要从其控制终端读取,则有终端驱动程序停止;如果设置了TOSTOP模式,同样用于写入。用户可以通过键入SUSP(control-z)字符来停止前台作业,程序可以通过向其发送SIGSTOP信号来停止任何作业。shell的职责是通知作业何时停止,将其通知用户,并提供允许用户交互的继续停止的作业并在前台和后台之前切换作业的机制。

进程的控制终端

进程的属性之一是它的控制终端。用fork创建的子进程从其父进程进程控制终端。这样,会话中的所有进程都将从会话leader(session leader)继承控制终端。具有终端控制权的会话leader(session leader)称为该终端的控制进程。

当单个进程调用setsid成为新会话的领导者时,它将与其控制终端断开连接。

访问控制终端

控制终端前台作业中的进程可以无限制的访问到这个终端。后台进程则不行。

当后台作业中的进程尝试从其控制终端读取时,通常会向该进程组发送SIGTTIN信号。他通常会导致该组中的所有进程停止(除非它们处理信号并且不会自行停止)。但是,如果读取过程忽略或阻止了此信号,则读取将失败,并显示EIO错误。

同样,当后台作业中的进程尝试写入其控制终端时,默认行为是向进程组发送SIGTTOU信号。但是,该行为可以通过本地模式标志的TOSTOP位进行修改(请参见本地模式)。如果未设置此位(默认设置),那么总是允许在不发送信号的情况下写入控制终端。如果SIGTTOU信号被写入过程忽略或阻止,则也允许写入。

孤儿进程组(Orphaned Process Groups)

当控制进程终止时,其终端将变成空闲状态,并可以在其上建立新的会话。

为防止出现问题,会将会话leader终止后仍继续运行的进程组标记为孤立进程组。

当一个进程组成为一个孤立组时,将向其下的子进程发送SIGHUP信号。通常,这导致进程终止。但是,如果程序忽略此信号或为其建立处理程序,则即使其控制进程终止,它也可以像在孤立进程组中一样继续运行。但它仍然无法再访问终端。

作业控制命令

通过作业控制可以将作业放到前台,后台,停止或者启动作业

命令 描述
jobs 列出全部命令
bg %n 将当前任务或者指定的任务放到后台,n是任务ID
fg % 将当前或指定的作业放到前台,n是作业ID
Control-Z 停止前台作业并将其作为已停止的作业放在后台

参考

https://www.gnu.org/software/libc/manual/html_node/Concepts-of-Job-Control.html#Concepts-of-Job-Control