信号的概念
取决于进程的要求,内核对Signal信号有如下三种行为:
- 忽略信号
- 抓住这个信号并且对这个信号进行处理
- 采取默认行为
信号标识符Signal Identifiers
Signals#1Signals#2
在Linux中, SIGINFO = SIGPWR | SIGIOT = SIGABRT | SIGPOLL = SIGLOST = SIGIO
- SIGABRT
函数abort将此信号发送给调用它的进程。然后,该进程终止并生成一个core file。在Linux中,在条件不满足时,断言assert()调用abort() 。 - SIGALRM
alarm()和setitimer()函数(带有ITIMER_REAL标志)将此信号发送给在警报过期时调用它们的进程。 - SIGBUS
当进程发生内存保护(这个会出发SIGSEGV)以外的硬件故障时,内核会引发此信号。 - SIGCHLD
每当进程终止或停止时,内核都会向进程的父进程发送此信号。由于默认情况下忽略SIGCHLD, 所以进程必须显式的捕获并处理他们感兴趣的子进程。此信号的处理程序通常调用wait()。 - SIGCONT
当进程在停止后恢复时,内核将此信号发送给进程。默认情况下,此信号将被忽略,但如果进程希望在继续之后进行一个特殊的行为则可以抓住他。此信号通常由希望刷新屏幕的终端或编辑器使用。 - SIGFPE
此信号表示任何算术异常,而不仅仅是那些与浮点操作相关的异常。 - SIGHUP
当会话session的终端断开时,内核将此信号发送给会话领导人seesion leader。 - SIGILL
当进程试图执行非法机器指令时,内核发送此信号。默认操作是终止进程并生成一个核心转储。进程可以选择捕获并处理SIGILL,但它们的行为在发生后没有定义。 - SIGINT
当用户输入中断字符(通常为Ctrl-C)时,此信号被发送到前台进程组中的所有进程。默认行为是终止;但是,进程可以选择 捕捉和处理这个信号,通常在结束前进行清理。 - SIGIO
处理异步IO - SIGKILL
这个信号是从kill()系统调用发送的;它的存在是为了向系统管理员提供一种绝对有效的方式来无条件地杀死一个进程。这个信号不能被捕获或忽略,而且它结果总是终止进程。 - SIGPIPE
如果进程写入管道,但读取器已终止,则内核将引发此信号。默认操作是终止进程,但是这个信号可能会被捕获和处理。 - SIGPROF
当与ITIMER_PROF标志一起使用时,setitimer()函数将在分析计时器过期时生成此信号。默认操作是终止进程。 - SIGPWR
这个信号与系统有关。在linux上,它表示低电池状态(例如在不间断电源或UPS中)。UPS监视守护进程将此信号发送给init,然后希望在电源断电前完成系统的清理和关闭。 - SIGQUIT
当用户提供终端退出字符(通常为Ctrl-)时,内核会为前台进程组中的所有进程发出此信号。默认操作是终止进程 生成一个core file。 - SIGSEGV
此信号的名称源于分段冲突,当它尝试无效的内存访问时,该信号被发送到进程。进程可以捕获并处理此信号,但默认操作是终止进程并生成一个core dump。 - SIGSTOP
这个信号只由kill()发送。它无条件地停止一个进程,不能被捕获或忽略。 - SIGSYS
内核在试图调用无效的系统调用时将此信号发送给进程。 - SIGTERM
此信号仅由kill()发送;它允许用户优雅地终止进程(默认操作)。进程可能会选择在终止之前捕获这个信号并清除,但被认为是粗鲁地抓住这个信号而不立即终止。 - SIGTRAP
当内核穿过断点时,将此信号发送给进程。通常,调试器捕获这个信号,而其他进程忽略它。 - SIGTSTP
当用户提供挂起字符(通常是Ctrl-Z)时,内核将此信号发送给前台进程组中的所有进程。 - SIGTTIN
当试图从其控制终端读取时,该信号被发送到后台的进程。默认操作是停止进程。 - SIGTTOU
当试图写入其控制终端时,该信号被发送到后台的进程。默认操作是停止进程。 - SIGURG
当out-of-band(OOB)数据到达套接字上时,内核将此信号发送给进程。带外数据超出了本书的范围。 - SIGUSR1 and SIGUSR2
这些信号可用于用户定义的用途。默认操作是终止进程。 - SIGVTALRM
当使用ITIMER_VIRTUAL标志创建的计时器过期时,setitimer()函数将发送此信号。 - SIGWINCH
当前台进程组中的所有进程的终端窗口大小发生变化时,内核将引发此信号。默认情况下,进程忽略此信号,但它们可能选择捕获 如果他们知道终端的窗口大小,就处理它。一个程序捕捉这个信号很好的例子, 是top-尝试调整其窗口时,它正在运行,并观察它的反应。 - SIGXCPU
当进程超过其软处理器限制时,内核将引发此信号。内核将继续每秒发出一次此信号,直到进程退出或超过其硬处理器限制为止。 一旦超出了硬限制,内核就会向进程发送SIGKILL。 - SIGXFSZ
当进程超过其文件大小限制时,内核将引发此信号。默认操作是终止进程,但如果捕获或忽略此信号,系统调用将返回-1,并设置errno-EFBIg。
基本信号管理
#include<signal.h>
typedef void(*sighandler_t)(int);
sighandler_t signal(int signo, sighandler_t handler);
还可以使用signal()指示内核忽略当前进程的给定信号,或者将信号重置为默认行为。这是使用处理。可以通过使用特殊的值给予handler参数来是实现。
- SIG_DEL
设置signal的行为默认化。 - SIG_IGN
忽略此参数。
等待一个或任意信号
可用于调试和编写演示代码片段,POSIX定义的pause()系统调用将进程置于睡眠状态,直到接收到处理或终止进程的信号为止 :
#include <unistd.h>
int pause(void);
pause()只在接收到信号时返回,在这种情况下信号被处理,而pause()返回−1并将errno设置为EINTR。如果内核引发一个被忽略的信号,进程就不会唤醒。
Examples
//test SIGINT
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
/* handler for SIGINT */
static void sigint_handler (int signo) {
/*
* Technically, you shouldn't use printf() in a
* signal handler, but it isn't the end of the
* world. I'll discuss why in the section
* "Reentrancy."
*/
printf ("Caught SIGINT!\n");
exit (EXIT_SUCCESS);
}
int main (void) {
/*
* Register sigint_handler as our signal handler
* for SIGINT.
*/
if (signal (SIGINT, sigint_handler) == SIG_ERR) {
fprintf (stderr, "Cannot handle SIGINT!\n");
exit (EXIT_FAILURE);
}
for (;;)
pause ();
return 0;
}
测试结果
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
/* handler for SIGINT and SIGTERM */
static void signal_handler (int signo) {
if (signo == SIGINT)
printf ("Caught SIGINT!\n");
else if (signo == SIGTERM)
printf ("Caught SIGTERM!\n");
else {
/* this should never happen */
fprintf (stderr, "Unexpected signal!\n");
exit (EXIT_FAILURE);
}
exit (EXIT_SUCCESS);
}
int main (void) {
/*
* Register signal_handler as our signal handler
* for SIGINT.
*/
if (signal (SIGINT, signal_handler) == SIG_ERR) {
fprintf (stderr, "Cannot handle SIGINT!\n");
exit (EXIT_FAILURE);
}
/*
* Register signal_handler as our signal handler
* for SIGTERM.
*/
if (signal (SIGTERM, signal_handler) == SIG_ERR) {
fprintf (stderr, "Cannot handle SIGTERM!\n");
exit (EXIT_FAILURE);
}
/* Reset SIGPROF's behavior to the default. */
if (signal (SIGPROF, SIG_DFL) == SIG_ERR) {
fprintf (stderr, "Cannot reset SIGPROF!\n");
exit (EXIT_FAILURE);
}
/* Ignore SIGHUP. */
if (signal (SIGHUP, SIG_IGN) == SIG_ERR) {
fprintf (stderr, "Cannot ignore SIGHUP!\n");
exit (EXIT_FAILURE);
}
for (;;)
pause ();
return 0;
}
执行和继承
子进程继承父进程的信号操作(ignore, default, handle)。
signal
/* handle SIGINT, but only if it isn't ignored */
if (signal (SIGINT, SIG_IGN) != SIG_IGN) {
if (signal (SIGINT, sigint_handler) == SIG_ERR)
fprintf (stderr, "Failed to handle SIGINT!\n");
}
/* handle SIGQUIT, but only if it isn't ignored */
if (signal (SIGQUIT, SIG_IGN) != SIG_IGN) {
if (signal (SIGQUIT, sigquit_handler) == SIG_ERR)
fprintf (stderr, "Failed to handle SIGQUIT!\n");
}
Mapping Signal Numbers to Strings
#define _GNU_SOURCE
#include <string.h>
char * strsignal (int signo);
static void signal_handler (int signo) {
printf ("Caught %s\n", sys_siglist[signo]);
}
网友评论