signal和sigaction 分析
1:signal 函数
原型: sighandler_t signal(int signum, sighandler_t handler) typedef void (*sighandler_t)(int);
描述:signal函数用来在进程中指定当一个信号到达进程后该做什么处理,主要的两种方式有忽略某些信号,(监听到SIGTERM/SIGINT)退出前的打扫工作。信号处理函数的handler有两个默认值,分别是SIG_IGN和SIG_DFL,表示忽略和默认行为。而且signal函数是阻塞的,比如当进程正在执行SIGUSR1信号的处理函数,此时又来一个SIGUSR1信号,signal会等到当前信号处理函数处理完后才继续处理后来的SIGUSR1,不管后来的多少个SIGUSR1信号,统一看做一个来处理。还有SIGKILL和SIGSTOP这两个信号是signal函数捕捉不到的。
注意:signal函数的行为与linux的版本有关(因为signal属于系统调用),所以移植性不好,可用sigaction函数来替代(sigaction是标准POSIX函数),而且sigaction还扩展了signal函数的功能。
2:sigaction函数
原型:int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
参数:
signum:信号值
act:信号的处理参数
oldact:保存信号上次安装时的处理参数(备份的作用)
返回值:0(success),-1(error)
struct sigaction { void (*sa_handler)(int); //信号处理函数 void (*sa_sigaction)(int, siginfo_t *, void *); sigset_t sa_mask; //信号屏蔽集 int sa_flags; void (*sa_restorer)(void);// 已废弃 };
要点:
信号阻塞:同signal函数类似,当正处于某个信号的处理函数中时,这个信号再次到达会被阻塞,待信号处理函数完成之后再处理。
sa_mask :信号屏蔽集,所谓屏蔽并不是忽略,屏蔽的时间段是在信号处理函数执行期间,一旦处理函数执行完毕将会重新唤醒此信号。
sa_flags :SA_RESTART:表示如果一个信号到来时进程正在执行某个系统调用(read/write等),执行完信号处理函数后返回系统调用,此系统调用会重新调用而不会出错。
实例1(sa_mask的使用):
#include <errno.h> #include <string.h> static void signal_handler(int sig) { printf("Got SIGUSR1\n"); printf("Processing SIGUSR1...\n"); sleep(5); printf("sleep over\n"); } int main(int argc, char** argv) { char buf[20]; int n = 0; printf("PID:%d\n", getpid()); struct sigaction act, oldact; act.sa_handler = signal_handler; sigemptyset(&act.sa_mask); sigaddset(&act.sa_mask, SIGINT);//把SIGINT信号加入屏蔽集 //act.sa_flags = SA_RESTART; if(sigaction(SIGUSR1, &act, &oldact) < 0) { perror("sigaction error"); return -1; } /* for(;;){ memset(buf, 0, 20); if((n = read(0, buf, 20)) < 0){ perror("read:"); } else{ buf[n] = '\0'; printf("%d type got, string:%s\n", n, buf); } } */ sleep(10); return 0; }
结果:
实例2(sa_flags=SA_RESTART):
#include <stdio.h> #include <unistd.h> #include <signal.h> #include <errno.h> #include <string.h> static void signal_handler(int sig) { printf("Got SIGUSR1\n"); printf("Processing SIGUSR1...\n"); sleep(5); printf("sleep over\n"); } int main(int argc, char** argv) { char buf[20]; int n = 0; printf("PID:%d\n", getpid()); struct sigaction act, oldact; act.sa_handler = signal_handler; sigemptyset(&act.sa_mask); sigaddset(&act.sa_mask, SIGINT); act.sa_flags = SA_RESTART;//设置SA_RESTART flag if(sigaction(SIGUSR1, &act, &oldact) < 0) { perror("sigaction error"); return -1; } memset(buf, 0, 20); if((n = read(STDIN_FILENO, buf, 20)) < 0){//行缓冲阻塞 perror("read:"); } else{ buf[n] = '\0'; printf("%d type got, string:%s\n", n, buf); } return 0; }
结果:
将 act.sa_flags = SA_RESTART; 这行代码屏蔽之后:
可以看到,SA_RESTART参数确实保证了被中断的系统调用在中断之后恢复之前的状态继续执行(系统调用中断出错应该报告Interrupted system call,不知我这里为何是core dumped?)。