APUE 学习笔记(七) 信号
1.信号是软件中断,提供一种异步处理事件的方法
很多事件产生信号:
(1)用户按下某些中断键,如 Ctrl + C键产生 SIGINT信号
(2)硬件异常产生信号,比如 除数为0,无效的内存引用
(3)进程调用kill函数可将信号发送给另一个进程
处理信号的三种方式:
(1)忽略此信号。大多数信号都采用这种方式处理,但是 SIGKILL和SIGSTOP决不能忽略
(2)捕捉信号。发生某种信号时,调用一个信号处理函数。SIGKILL和SIGSTOP信号不能被捕捉
(3)执行系统默认动作。大多数是终止进程
SIGCHLD:进程终止时向其父进程发送的信号,默认为忽略。父进程也可以捕捉该信号。
SIGFPE: 算术运算异常,如除数为0
SIGINT: 用户按下中断键,一般为 Ctrl + C
SIGSEGV:无效的内存引用
2.中断的系统调用
如果进程在执行一个低速系统调用而阻塞期间捕捉到一个信号,则该系统调用就中断不再执行
低速系统调用:读写某些类型文件(管道、终端、网络套接字)
again: if ((n = read(fd, buf, BUFSIZE)) < 0) { if (errno == EINTR) goto again; /* just an interrupted system call */ /* handle other errors */ }
3.使用longjmp,带超时限制调用read
#include <stdio.h> #include <setjmp.h> #include <unistd.h> #include <signal.h> static jmp_buf env_alarm; static void sig_alarm(int signo) { longjmp(env_alarm, 1); } int main(int argc, char* argv[]) { char buf[4096]; if (signal(SIGALRM, sig_alarm) == SIG_ERR) { fprintf(stderr, "signal(SIGALRM) error\n"); } if (setjmp(env_alarm) != 0) { fprintf(stderr, "read timeout\n"); return -1; } alarm(5); int nread = 0; if ((nread = read(STDIN_FILENO, buf, 4096)) < 0) { fprintf(stderr, "read error\n"); } alarm(0); write(STDOUT_FILENO, buf, nread); return 0; }
4.sigaction函数
sigaction函数用来检查或修改与指定信号相关联的处理动作
struct sigaction { void (*sa_handler)(int); /* addr of signal handler */ sigset_t sa_mask; /* addtional signals to block */ int sa_flags; /* signal options */ void (*sa_sigaction)(int, siginfo_t *, void *); /* alternate handler */ };
posix使用sigaction实现signal(原有的signal函数语义不可靠)
typedef void Sigfunc(int); Sigfunc* signal(int signo, Sigfunc* func) { struct sigaction newact, oldact; newact.sa_handler = func; sigemptyset(&newact.sa_mask); newact.sa_flags = 0; if (signo == SIGALRM) { #ifdef SA_INTERRUPT newact.sa_flags |= SA_INTERRUPT; #endif } else { #ifdef SA_RESTART newact.sa_flags |= SA_RESTART; #endif } if (sigaction(signo, &newact, &oldact) < 0) { return (SIG_ERR); } return oldact.sa_handler; }