实验3 信号 |
1 信号的概念 信号是发送给进程的特殊消息,特点是具有异步性。 |
2 简单信号程序编程思路: 首先设置act(就是那个struct sigaction 结构体,具体有三项 a 处理函数void XXXX(int ) 该函数XXXX 必须满足返回类型为void,一个参数类型为int |
b 掩码 c 选项 |
一般清空下用sigemptyset(sigset_t *set)清空 一般情况下设为0 |
简单信号处理 下面的程序设置了SIGINT 的(Ctrl+C)的处理函数 #include <stdio.h> #include <stdlib.h> #include <unistd.h> struct sigaction act; |
int num=0; void catchctrlc(int signo) { |
printf("第%d 次进入信号处理程序\r\n",num+1); num++; if(num>=9) act.sa_handler=SIG_DEF; sigaction(SIGINT,&act,NULL); |
printf("信号处理程序结束\r\n"); |
} |
int main(int argc,char **argv) { |
act.sa_handler=catchctrlc;//设置信号处理程序 act.sa_flags=0;//无特殊选项 sigemptyset(&act.sa_mask);//清空act 的sa_mask 域,即信号掩码 //其实也可以用act.sa_mask=0;代替 |
sigaction(SIGINT,&act,NULL);//设置SIGINT 的处理函数 |
for(;;); |
} |
按10 次Ctrl+C 就可以恢复缺省的处理方式:即默认退出(SIG_DEF) |
请编写一个程序是休眠10 秒钟,忽略掉Ctrl+Z 信号,即SIGTSTP(有些平台对Ctrl+Z 不 支持) |
2 计时器信号 下面的程序每隔1 秒中,发送一次sigalarm 信号,从而执行相应的函数。 #include <signal.h> #include <sys/time.h> #include <string.h> #include <stdlib.h> #include <stdio.h> struct itimerval val_alarm = {.it_interval.tv_sec=1, |
.it_interval.tv_usec=0, .it_value.tv_sec=1, .it_value.tv_usec=0 }; |
void sig_handler(int sig) { printf("Timer has expired,elapsed %d,seconds \r\n",val_alarm.it_value.tv_sec); |
} |
int main() |
{ |
struct sigaction sa; memset(&sa,0,sizeof(sa)); sa.sa_hander=&sig_handler; int ret= sigaction(SIGALARM,&sa,NULL); setitimer(ITIMER_REAL,&val_alarm,NULL); while(1); return 0; |
} |
3 信号掩码 |
有时程序在执行时,需要阻止某些信号的接受,可以利用信号掩码来进行对信号的阻止 #include <signal.h> #include <unistd.h> #include <sys/types.h> void my_op(int); main() { |
sigset_t new_mask; struct sigaction act; sigemptyset(&act.sa_mask); act.sa_flags=0; act.sa_handler=my_op; |
if(sigaction(SIGINT,&act,NULL)) |
//注册信号SIGINT 处理函数为my_op |
{ |
printf("install signal SIGINT error\n"); |
} |
if(sigaction(SIGQUIT,&act,NULL)) |
//注册信号SIGQUIT 处理函数为my_op |
{ |
printf("install signal SIGQUIT error\n"); |
} printf("please send signal: kill -s %d %d\n",SIGQUIT, getpid()); sigemptyset(&new_mask); if(sigprocmask(0, NULL,&new_mask) < 0) |
//查询当前进程被挂起的信号存入new_mask 中, |
//可用sigpending(&new_mask)代替 |
{ |
printf("get pending mask error\n"); |
} |
sigaddset(&new_mask,SIGQUIT); |
//将信号SIGQUIT,添加到信号集new_mask 中, //也就是在原有的信号掩码中加入信号SIGQUIT。 sigsuspend(&new_mask); |
//将当前的信号掩码替换成new_mask, //也就是把信号SIGQUIT 给阻塞后,当前进程挂起。 //必须等待除了未阻塞的信号到来才恢复成原来的信号掩码 exit(0); |
} |
void my_op(int signum) { if ( signum == SIGINT ) { printf("SIGINT\n"); } else if ( signum == SIGQUIT ) { printf("SIGQUIT\n"); |
} return; |
} ---------------------------------------------------------------------- |
编译该程序,并运行。在另一终端向该进程发送信号(运行kill -s SIGQUIT pid,SIGQUIT 以 及pid 具体多少看程序的输出),一直未见到应该打印的信息"SIGQUIT\n",这就说明 |
SIGQUIT 信号被阻塞了,当发送SIGINT 信号时给该程序时,因为SIGINT 信号并未阻塞, 所以打印了"SIGINT\n",紧接着sigsuspend 恢复原来的信号掩码(原来的信号掩码并未阻塞 SIGQUIT 信号),最后才打印了刚开始接收到的SIGQUIT 信号的信息息"SIGQUIT\n"。具体 情况如下: |
情况一: 1、程序运行后,执行到sigsuspend 函数,用new_mask 替换原来的信号掩码,即SIGQUIT 信号被阻塞。 2、开启另一终端,执行kill -s SIGQUIT pid 程序结果如下: please send signal: kill -s 3 4026 |
3、这就说明SIGQUIT 信号被阻塞了,未见到应该打印的信息"SIGQUIT\n",程序任在被挂 起状态。 情况二: 1、程序运行后,执行到sigsuspend 函数,用new_mask 替换原来的信号掩码,即SIGQUIT 信号被阻塞。 2、开启另一终端,执行kill -s SIGQUIT pid 程序结果如下: please send signal: kill -s 3 4026 |
3、这就说明SIGQUIT 信号被阻塞了,未见到应该打印的信息"SIGQUIT\n"",程序任在被挂 起状态。 |
4、继续在另一终端上,执行kill -s SIGINT pid 程序结果如下: please send signal: kill -s 3 4026 SIGINT SIGQUIT |
程序退出 5、这说明sigsuspend 恢复原来的信号掩码(原来的信号掩码并未阻塞SIGQUIT 信号),最 后才打印了刚开始接收到的SIGQUIT 信号的信息息"SIGQUIT\n",打印结束程序退出。 |
#include <signal.h> #include <unistd.h> #include <sys/types.h> volatile quitflag; void my_op(int); main() { sigset_t new_mask,old_mask,zero_mask; struct sigaction act; |
quitflag = 0; sigemptyset(&act.sa_mask); act.sa_flags=0; act.sa_handler=my_op; |
if(sigaction(SIGINT,&act,NULL)) { |
//注册信号SIGINT 处理函数为my_op |
printf("install signal SIGINT error\n"); |
} |
if(sigaction(SIGQUIT,&act,NULL)) //注册信号SIGQUIT 处理函数为my_op { printf("install signal SIGQUIT error\n"); } printf("please send signal: kill -s %d %d\n",SIGQUIT, getpid()); sigemptyset(&new_mask); |
sigaddset(&new_mask,SIGQUIT); //将信号SIGQUIT,添加到空信号集new_mask 中 |
if(sigprocmask(SIG_BLOCK, &new_mask,&old_mask)) //把信号SIGQUIT 给阻塞 { printf("block signal SIGQUIT error\n"); } |
sigemptyset(&zero_mask);//清空信号集zero_mask while(quitflag == 0) { sigsuspend(&zero_mask); |
//将当前的信号掩码替换成空信号掩码zero_mask, |
//也就是没有任何信号被阻塞,当前进程挂起。 //等待任意信号到来就可恢复成原来的信号码,但是这里有个循环 //判断当quitflag == 0 时继续挂起当前进程,这里除了SIGQUIT 信号 //外其它信号都不可以改变quitflag 的值 |
} |
if(sigprocmask(SIG_SETMASK,&old_mask,NULL)<0) //程序继续运行后恢复进程原 来的信号掩码 { printf("unblock signal error\n"); |
} |
exit(0); |
} |
void my_op(int signum) { if ( signum == SIGINT ) { printf("SIGINT\n"); |
} else if ( signum == SIGQUIT ) { printf("SIGQUIT\n"); quitflag = 1; } |
return; |
} ---------------------------------------------------------------------- |
这里程序的情况分三种: 情况一: 1、程序运行后,执行到sigsuspend 函数,用空信号掩码zero_mask 替换原来的信号掩码, 即不阻塞任何信号。 2、开启另一终端,执行kill -s SIGINT pid 程序结果如下: please send signal: kill -s 3 4026 SIGINT |
3、程序任在被挂起状态,这就说明还在循环中,因为SIGINT 信号的处理函数不能更改 quitflag 的值,仍然不阻塞任何信号。 情况二: 1、程序运行后,执行到sigsuspend 函数,用空信号掩码zero_mask 替换原来的信号掩码, 即不阻塞任何信号。 2、开启另一终端,执行kill -s SIGINT pid 程序结果如下: please send signal: kill -s 3 4026 SIGINT |
3、程序任在被挂起状态,这就说明还在循环中,因为SIGINT 信号的处理函数不能更改 |
quitflag 的值,仍然不阻塞任何信号。 4、继续在另一终端上,执行kill -s SIGQUIT pid 程序结果如下: please send signal: kill -s 3 4026 SIGINT SIGQUIT |
执行完后程序退出 5、为什么打印了“SIGQUIT\n”?不是被阻塞了么?原因是:sigsuspend 不阻塞任何信号, 所以呢,就执行了SIGQUIT 的处理函数,改变了quitflag 的值,quitflag = 1,打印"SIGQUIT\n"; 然后退出循环。紧接着sigsuspend 恢复“原来的”信号掩码(这里的“原来的信号掩码”是 包含阻塞SIGQUIT 信号的),最后把信号掩码设置成最初的样子old_mask,程序退出。 |
情况三: 1、程序运行后,执行到sigsuspend 函数,用空信号掩码zero_mask 替换原来的信号掩码, 即不阻塞任何信号。 2、开启另一终端,执行kill -s SIGINT pid 程序结果如下: please send signal: kill -s 3 4026 SIGINT |
3、程序任在被挂起状态,这就说明还在循环中,因为SIGINT 信号的处理函数不能更改 quitflag 的值,仍然不阻塞任何信号。 4、继续在另一终端上,执行kill -s 40 pid 程序结果如下: please send signal: kill -s 3 4026 SIGINT Real-time signal 5 |
执行完后程序退出 5、为什么打印了“Real-time signal 5\n”?原因是:sigsuspend 不阻塞任何信号,所以呢, |
就执行了40 的默认处理函数。那为什么退出了循环了?应该没改变quitflag 的值啊?实际 上,40 信号的默认处理函数是打印一行字符后就直接终止程序了,所以后面的代码都没执 行了。想要改变就必须重新注册40 的处理函数。 |