1.什么是信号
信号是Linux系统响应某些条件而产生的一个事件,接收到该信号的进程会执行相应的操作。
2.信号的产生
1)由硬件产生,如从键盘输入Ctrl+C可以终止当前进程
2)由其他进程发送,如可在shell进程下,使用命令 kill -信号标号 PID,向指定进程发送信号。
3)异常,进程异常时会发送信号
3.信号的处理
信号是由操作系统来处理的,说明信号的处理在内核态。
信号不一定会立即被处理,此时会储存在信号的信号表中。
处理过程示意图:
由上图中可看出信号有三种处理方式:
1)忽略
2)默认处理方式:操作系统设定的默认处理方式
3)自定义信号处理方式:可自定义信号处理函数
4.自定义信号处理方式
1)signal函数
原型:
void (*signal(int sig, void (*func)(int)))(int);
sig:信号值
func:信号处理函数指针,参数为信号值
代码示例如下:
#include <signal.h> #include <stdio.h> void ouch(int sig) { printf(" OUCH! - I got signal %d ", sig); //恢复终端中断信号SIGINT的默认行为 (void) signal(SIGINT, SIG_DFL); } int main() { //改变终端中断信号SIGINT的默认行为,使之执行ouch函数 //而不是终止程序的执行 (void) signal(SIGINT, ouch); while(1) { printf("Hello World! "); sleep(1); } return 0; }
输出结果:
2)sigaction函数
原型:
int sigaction(int sig,const struct sigaction *act,struct sigaction *oact);
sig:信号值
act:指定信号的动作
oact:保存原信号的动作
sigaction结构体的定义如下:
void (*)(int) sa_handler;处理函数指针,相当于signal函数的func参数。
sigset_t sa_mask;处理过程中,屏蔽对sa_mask信号集的处理,sa_mask可以消除信号间的竞态。
int sa_flags;信号处理修改器:处理函数执行完后,信号处理方式修改。如SA_RESETHAND,将信号处理方式重置为SIG_DFL
代码示例如下:
#include <stdio.h> #include <signal.h> void ouch(int sig) { printf(" OUCH! - I got signal %d ", sig); } int main() { struct sigaction act; act.sa_handler = ouch; //创建空的信号屏蔽字,即不屏蔽任何信息 sigemptyset(&act.sa_mask); //使sigaction函数重置为默认行为 act.sa_flags = SA_RESETHAND; sigaction(SIGINT, &act, 0); while(1) { printf("Hello World! "); sleep(1); } return 0; }
输出结果:
4.信号的发送
1)kill函数
int kill(pid_t pid,int signo);
pid:进程ID
signo:信号值
2)raise函数:只能向当前进程发信号
int raise(int signo);
signo:信号值
3)abort函数:发送SIGABRT信号,可以让进程异常终止
void abort(void);
4)alarm函数:发送SIGALRM闹钟信号
unsigned int alarm(unsigned int seconds);
5.信号的阻塞
阻塞是阻止进程收到该信号,此时信号处于未决状态,放入进程的未决信号表中,
当解除对该信号的阻塞时,未决信号会被进程接收。
1)阻塞信号
原型:
int sigprocmask(int how,const sigset_t *set,sigset_t *oset);
how:设置block阻塞表的方式
a.SIG_BLOCK:将信号集添加到block表中
b.SIG_UNBLOCK:将信号集从block表中删除
c.SIG_SETMASK:将信号集设置为block表
set:要设置的集合
oset:设置前保存之前block表信息
2)获取未决信号
前面已经讲过,阻塞的信号处于未决的状态,会放入进程的未决信号表。
原型:
int sigpending(sigset_t *set);
set:out型参数,会将获得的当前进程的pending未决表中的信号集传入。
代码示例如下:
#include <stdio.h> #include <sys/signal.h> #include <sys/types.h> #include <signal.h> void func(int num) { printf("catch signal number is %d",num); } void printfpendingsignal(sigset_t *set) { int i; for(i=1;i<32;++i) { if(sigismember(set,i)) { printf("1"); } else { printf("0"); } } printf(" "); } int main() { sigset_t s,p,o; signal(SIGINT,func); sigemptyset(&s); sigemptyset(&p); sigemptyset(&o); sigaddset(&s,SIGINT); sigprocmask(SIG_SETMASK,&s,&o); int count=0; while(1) { sigpending(&p); printfpendingsignal(&p); sleep(1); if(count++==10) { printf("recover! "); sigprocmask(SIG_SETMASK,&o,NULL); } } return 0; }
输出结果:
6.信号处理函数的安全问题
如果信号处理过程中被中断,再次调用,然后返回到第一次调用时,要保证操作的正确性。
这就要求信号处理函数必须是可重入的。
可重入函数表如下:
7.一些常见的信号
如果进程接收到上面的这些信号,又没有安排捕获它,进程就会终止。
其他的一些信号如下: