* SIGCHLD:子进程已停止或终止。
* SIGCONT:如果进程已停止,则使其继续运行,否则忽略。
* SIGSTOP:停止信号(不能被捕捉或忽略)。
* SIGTSTP:交互式停止信号。
* SIGTTIN:后台进程组成员读控制终端。
* SIGTTOU:后台进程组成员写控制终端。
除 SIGCHLD 以外,大多数应用程序并不处理这些信号,交互式 shell 则通常会处理这些信号的所有工作。当键入挂起字符(通常是 Ctrl+Z)时,SIGTSTP 就被送至前台进程组的所有进程;当通知 shell 在前台或后台恢复运行一个作业时,shell 就向该作业中的所有进程发送 SIGCONT 信号。当对一个停止的进程产生一个 SIGCONT 信号时,该进程就继续,即使该信号是被阻塞或忽略的。
下面这个程序演示了当一个程序处理作业控制时通常所使用的规范代码序列。该程序只是将其标准输入复制到标准输出,而在信号处理程序中以注释形式给出了管理屏幕的程序(如 VI)所执行的典型操作。
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <signal.h> #define BUFFER 1024 static void sig_tstp(int signo){ // signal handler for SIGTSTP /* ... move cursor to lower left corner, reset tty mode ... */ /* Unblock SIGTSTP, since it's blocked while we're handling it */ sigset_t mask; sigemptyset(&mask); sigaddset(&mask, SIGTSTP); sigprocmask(SIG_UNBLOCK, &mask, NULL); signal(SIGTSTP, SIG_DFL); // reset disposition to default printf("send SIGTSTP\n"); kill(getpid(), SIGTSTP); // and send the signal to ourself printf("received SIGCONT\n"); /* We won't return from the kill untill we're continued */ signal(SIGTSTP, sig_tstp); // reestablish signal handler /* ... reset tty mode, redraw screen ... */ } int main(void){ ssize_t n; char buf[BUFFER]; /* 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, BUFFER)) > 0){ if(write(STDOUT_FILENO, buf, n) != n) printf("write error\n"); } if(n < 0) printf("read error\n"); exit(0); }
这里仅当 SIGTSTP 信号的配置是 SIG_DFL 时才安排捕捉该信号的理由是:init 会将 SIGTSTP、SIGTTIN 和 SIGTTOU 设置为 SIG_IGN,这种配置将被所有登录 shell 继承。所以当此程序由不支持作业控制的 shell(如 /bin/sh)启动时,此信号的配置将被设置为 SIG_IGN。只有作业控制 shell 才应将这 3 个信号重新设置为 SIG_DFL。
程序运行结果如下。
$ ./jobCtrlDemo.out abcde # 读取输入 abcde # 输出 ^Zsend SIGTSTP # Ctrl-Z 发送 SIGTSTP 信号挂起进程 [1]+ Stopped ./jobCtrlDemo.out $ ps # 查看进程 ID PID TTY TIME CMD 23893 pts/1 00:00:00 jobCtrlDemo.out 23914 pts/1 00:00:00 ps 82002 pts/1 00:00:05 bash $ $ kill -SIGCONT 23893 # 发送 SIGCONT 信号使进程继续运行 received SIGCONT [1]+ Stopped ./jobCtrlDemo.out
当键入挂起字符时,进程接到 SIGTSTP 信号,然后调用该信号处理程序。此时,应当进行与终端有关的处理(如将光标移到左下角、恢复终端工作方式等)。在将 SIGTSTP 重置为默认值(停止该进程),并解除了对此信号的阻塞(因为正在捕捉一个信号期间系统会自动地阻塞该信号)后,进程向自己发送同一信号 SIGTSTP,使系统停止该进程。仅当该进程接到 SIGCONT 信号后才会如同从 kill 函数返回一样继续运行。最后再将 SIGTSTP 重置为捕捉,并做一些终端处理工作(如重新绘制屏幕)。