控制信号

控制信号

POSIX.1考虑使用六种任务控制信号:

SIGCHLD

子进程已经停止或终止。

SIGCONT

如果进程已经停止那么继续进程。

SIGSTOP

停止信号(不能被捕获或忽略)。

SIGTSTP

互斥停止信号(Interactive stop signal)

SIGTTIN

后台进程组成员读取控制终端。

SIGTTOU

由后台进程组成员写控制终端。

除了SIGCHLD信号外多数应用程序并不处理这些信号:交互shell通常去处理这些信号。当我们键入挂起字符(一般是Ctrl+Z),SIGTSTP信号被发送到所有前台进程组中。当我们告诉shell在前台或后台保留一个任务时,shell会向所有工作任务发送一个SIGCONT信号。类似的,如果SIGTTINSIGTTOU被发送到进程,这个进程默认情况下会被停止,并且任务控制shell记录下这个并通知我们。

一个例外是管理终端的进程:如vi编辑器。它要知道用户要挂起它,所以它能恢复终端到它开始时的状态。同样,当它恢复到前台时,vi编辑器需要设置终端状态到它想要的状态,并且它需要重新绘制屏幕。后面的例子会显示程序如何像vi一样处理。

在任务控制信号中有一些互斥信号。当某个进程收到四种停止信号(SIGTSTP, SIGSTOP, SIGTTINSIGTTOU)时,该进程任何处于pending状态的SIGCONT信号会被抛弃。类似的当某个进程收到SIGCONT信号,任何pending状态的停止信号也都会被抛弃。

注意如果进程已经停止,SIGCONT的默认动任是继续进程;其它情况会忽略。一般地,我们不必为这个信号做任何事情。当SIGCONT信号为某个已经停止的进程产生时,这个进程就会继续运行,即使这个信号被阻塞或忽略

例子:

Figure10.30的程序演示了当一个程序处理任务控制的正常代码顺序。这个程序简单的复制了它的标准输入到它的标准输出,但是在信号处理函数中程序管理了屏幕(注释写明了处理方法)。当Figure10.30程序开始,仅当信号的预置行为(disposition)是SIG_DFL时,它才会去捕获SIGTSTP信号。理由是当程序被不支持任务控制的shell执行时(如/bin/sh),该信号的预置行为(disposition)将会是SIG_IGN。实际上,shell不会显示的忽略这个信号;init设置了三个任务控制信号(SIGTSTP, SIGTTINSIGTTOU)的部署是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 ... */
}

发表评论