控制信号
POSIX.1考虑使用六种任务控制信号:
|
除了SIGCHLD信号外多数应用程序并不处理这些信号:交互shell通常去处理这些信号。当我们键入挂起字符(一般是Ctrl+Z),SIGTSTP信号被发送到所有前台进程组中。当我们告诉shell在前台或后台保留一个任务时,shell会向所有工作任务发送一个SIGCONT信号。类似的,如果SIGTTIN或SIGTTOU被发送到进程,这个进程默认情况下会被停止,并且任务控制shell记录下这个并通知我们。
一个例外是管理终端的进程:如vi编辑器。它要知道用户要挂起它,所以它能恢复终端到它开始时的状态。同样,当它恢复到前台时,vi编辑器需要设置终端状态到它想要的状态,并且它需要重新绘制屏幕。后面的例子会显示程序如何像vi一样处理。
在任务控制信号中有一些互斥信号。当某个进程收到四种停止信号(SIGTSTP, SIGSTOP, SIGTTIN或SIGTTOU)时,该进程任何处于pending状态的SIGCONT信号会被抛弃。类似的当某个进程收到SIGCONT信号,任何pending状态的停止信号也都会被抛弃。
注意如果进程已经停止,SIGCONT的默认动任是继续进程;其它情况会忽略。一般地,我们不必为这个信号做任何事情。当SIGCONT信号为某个已经停止的进程产生时,这个进程就会继续运行,即使这个信号被阻塞或忽略。
例子:
Figure10.30的程序演示了当一个程序处理任务控制的正常代码顺序。这个程序简单的复制了它的标准输入到它的标准输出,但是在信号处理函数中程序管理了屏幕(注释写明了处理方法)。当Figure10.30程序开始,仅当信号的预置行为(disposition)是SIG_DFL时,它才会去捕获SIGTSTP信号。理由是当程序被不支持任务控制的shell执行时(如/bin/sh),该信号的预置行为(disposition)将会是SIG_IGN。实际上,shell不会显示的忽略这个信号;init设置了三个任务控制信号(SIGTSTP, SIGTTIN和SIGTTOU)的部署是SIG_IGN。这个预置行为(disposition)会被所有登陆shell继承。只有带任务控制的shell会重置这些信号的预置行为(disposition)到SIG_DFL。
当我们键入挂起字符,进程收到SIGTSTP信号,之后信号处理函数被调用。这时,我们可以做任何终端相关的事情:移动光标到左下角, 恢复终端模式, 等等。我们然后发送我们自己的相同信号,SIGTSTP,之后重置它的预置行为到默认(停止进程)并且解锁信号。我们必须解锁它,因为我们要正确处理相同的信号,并且当该信号被捕获后系统会自动的锁定它。这时,系统停止进程。只有当进程收到(通常是从任务控制SHELL,使用fg命令进行交互)SIGCONT信号时才会继续运行。我们不捕获SIGCONT,它的预置行为是继续停的进程;当这发生的时候,程序继续运行就像从kill函数返回一样。(When this happens, the program continues as though it returned from the kill function.)当程序继续运行时,我们为SIGTSTP信号重置预置行为并且执行任何我们想执行的命令(如,我们可以重绘屏幕)。
Figure 10.30. How to handle SIGTSTP#include "apue.h"
#define BUFFSIZE 1024
static void sig_tstp(int);
int
main(void)
{
int n;
char buf[BUFFSIZE];
/*
* Only catch SIGTSTP if we're running with a job-control shell.
*/
if (signal(SIGTSTP, SIG_IGN) == SIG_DFL)
signal(SIGTSTP, sig_tstp);
while ((n = read(STDIN_FILENO, buf, BUFFSIZE)) > 0)
if (write(STDOUT_FILENO, buf, n) != n)
err_sys("write error");
if (n < 0)
err_sys("read error");
exit(0);
}
static void
sig_tstp(int signo) /* signal handler for SIGTSTP */
{
sigset_t mask;
/* ... move cursor to lower left corner, reset tty mode ... */
/*
* Unblock SIGTSTP, since it's blocked while we're handling it.
*/
sigemptyset(&mask);
sigaddset(&mask, SIGTSTP);
sigprocmask(SIG_UNBLOCK, &mask, NULL);
signal(SIGTSTP, SIG_DFL); /* reset disposition to default */
kill(getpid(), SIGTSTP); /* and send the signal to ourself */
/* we won't return from the kill until we're continued */
signal(SIGTSTP, sig_tstp); /* reestablish signal handler */
/* ... reset tty mode, redraw screen ... */
}
|