spacer.gif实验3 信号

1 信号的概念

信号是发送给进程的特殊消息,特点是具有异步性。

2 简单信号程序编程思路:

首先设置act(就是那个struct  sigaction 结构体,具体有三项

a 处理函数void XXXX(int  ) 该函数XXXX 必须满足返回类型为void一个参数类型为int

b 掩码

c 选项

一般清空下用sigemptysetsigset_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(有些平台对CtrlZ

支持)

spacer.gif2 计时器信号

下面的程序每隔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

spacer.gif{

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 pidSIGQUIT

pid 具体多少看程序的输出),一直未见到应该打印的信息"SIGQUIT\n",这就说明

spacer.gifSIGQUIT 信号被阻塞了,当发送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

spacer.gifprintf("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;

}

spacer.gifreturn;

}

----------------------------------------------------------------------

这里程序的情况分三种:

情况一:

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 的处理函数。