信号处理过程:
中断源-》中断屏蔽-》保护现场-》中断处理程序-》中断恢复
信号称为软中断
//kill -l 查看所有signal信号 共计64个信号
//man 7 signal 查看signal信号的意思
//信号提供了一种异步处理的一种能力;
//action 是默认操作 在man 7 signal里面的表里面
//ctrl + \ 可以杀死3) SIGQUIT 退出程序
//signal(SIGINT,SIG_DFL) //从新关联了默认程序
一)信号与中断
(1)信号与中断的相似点
1)都采用异步通讯
2)都可以处理相应的信号(中断)服务程序
3)都可以返回
4)对信号或中断信号都可以进行屏蔽
(2)信号与中断的区别
1)中断有优先级,信号没有优先级
2)信号是用户态运行,中断是内核态运行
3)中断相应是及时的,信号是有延时的!
(3)信号的三种相应
1)忽略信号
2)捕获并处理信号
3)执行默认操作
二)signal信号的注册及简单应用
#include
#include
#include
#include
#include
using namespace std;
#define ERR_EXIT(m) \
do \
{\
perror(m);\
exit(EXIT_FAILURE);\
}while(0)
void handler(int num)
{
cout<<"输出 num:"<<num<<endl;
}
int main(void)
{
// signal(SIGINT,handler); //安装(注册)一个信号,就是说将一个SIGINT信号和handler函数关联起来
if(signal(SIGINT,handler) == SIG_ERR) //也可以对返回值进行处理
{
ERR_EXIT("signal error");
}
while(1);
return 0;
}
小插曲:
/ /queue::iterator it1; //单端队列不可以遍历,没有迭代器
deque::iterator it2; //双端队列可以遍历
string::iterator i3; //string肯定可以遍历
二)信号分为可靠信号和不可靠信号
SIGRT runtime 实时信号也叫可靠信号 从34到64之后
1) signal 向父进程发送一个信号
#include
#include
#include
#include
#include
using namespace std;
#define ERR_EXIT(m) \
do \
{\
perror(m);\
exit(EXIT_FAILURE);\
}while(0)
void handler(int num)
{
cout<<"输出 num:"<<num<<endl;
}
int main(void)
{
signal(SIGUSR1,handler); //安装(注册)一个信号,就是说将一个SIGINT信号和handler函数关联起来
pid_t pid = fork();
if(pid == 0)
{
kill(getppid(),SIGUSR1);//向父进程发送一个信号
//pid = getpgrp();
//kill(-pid,SIGUSR1);//向进程组发送一个信号;
exit(EXIT_SUCCESS);
}
int n = 2;
do
{
n = sleep(n); //剩余时间
} while (n > 0);
return 0;
}
三)pause函数在signal中的使用
1) pause函数使用方法;
//pause会睡眠,使调用者进程挂起,直到信号被唤醒才可以继续运行
#include
#include
#include
#include
#include
using namespace std;
void handler(int num)
{
cout<<"输出 num:"<<num<<endl;
sleep(1);
}
int main(void)
{
signal(SIGINT,handler); //安装(注册)一个信号,就是说将一个SIGINT信号和handler函数关联起来
while(1)
{
pause(); //pause函数进入中断睡眠状态
cout<<"pause return"<<endl;
}
return 0;
}
四)更多信号学习
alarm SIGALRM //发送一个时钟信号
setitimer SIGALRM SIGVTALRM SIGGPROF
abort SIGABRT
给一个a.out的进程发送一个alrm信号的方法:
kill -ALRM ps aux|grep a.out|grep -v vi|grep -v grep|awk '{print $2}'
alarm函数的使用方法:
//alarm函数使用方法
#include
#include
#include
#include
#include
using namespace std;
void handler(int num)
{
cout<<"输出 num:"<<num<<endl;
sleep(1);
}
int main(void)
{
signal(SIGALRM,handler); //安装(注册)一个信号,就是说将一个SIGINT信号和handler函数关联起来
alarm(1);
while(1)
{
pause(); //pause会睡眠,使调用者进程挂起,直到信号被唤醒才可以继续运行
alarm(1); //每个1妙发送一个信号
}
return 0;
}
//在信号处理函数中,应使用可重入函数!!!
//信号从产生到递达的过程;
man sigpending 获取当前信号的未决状态集!
//信号集操作函数如下:
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset(sigset_t *set, int signum);
int sigdelset(sigset_t *set, int signum);
int sigismember(const sigset_t *set, int signum);
信号产生的过程:
信号从产生状态到递达状态,中间经过了一个未决状态;
未决状态产生的条件是信号发生的时候,信号被阻塞,如果阻塞被解除,则信号会变成递达状态;
五)sigprocmask sigemptyset sigaddset sigpending等函数的综合应用
测试代码如下:
#include
#include
#include
#include
#include
using namespace std;
void handler(int sig)
{
if(sig == SIGINT)
cout<<"recv sig:"<<sig<<endl;
else if(sig == SIGQUIT)
{
sigset_t uset;
sigemptyset(&uset);
sigaddset(&uset,SIGINT);
sigprocmask(SIG_UNBLOCK,&uset,NULL); //去除屏蔽,
}
}
void sigprint(sigset_t* set)
{
int i;
for(i = 1; i<NSIG; i++)
{
if(sigismember(set,i))
putchar('1');
else
{
putchar('0');
}
}
printf("\n"); //没有这句话,上面的不会正确输出,因为数据都在缓存里面呢!
}
int main(void)
{
signal(SIGINT,handler); //安装(注册)一个信号,就是说将一个SIGINT信号和handler函数关联起来
signal(SIGQUIT,handler);
sigset_t pset; //p pending 的意思
sigset_t bset; //b应该是block 阻塞的意思
sigemptyset(&bset);
sigaddset(&bset,SIGINT); //把SIGINT信号加入到集合中
sigprocmask(SIG_BLOCK,&bset,NULL); //把SIGINT信号使能阻塞,模拟信号触发后,会屏蔽,这样就不会递达
while(1)
{
sigpending(&pset);
sigprint(&pset);
sleep(1);
}
return 0;
}
上述代码功能测试操作:
ctrl+c,差生sigint 信号,此时会产生屏蔽,“1”,ctrl+,会去除屏蔽,
killall a.out //杀死a.out进程
六)sigaction信号安装函数的使用方法
//信号被屏蔽了,就是被阻塞了
//按ctrl+c,则会输出结果
//sigaction即是结构体,又是函数名!
用sigaction安装sigint信号的案例
#include
#include
#include
#include
#include
using namespace std;
void handler(int sig)
{
cout<<"recv sig:"<<sig<<endl;
}
int main(void)
{
struct sigaction act;
act.sa_handler = handler; //函数指针
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGINT,&act,NULL); //注册SIGINT信号函数
while(1)
{
pause();
}
return 0;
}
七)sigaction的sa_mask的使用方法:
注:sa_mask如何影响sigaction函数的行为!
#include
#include
#include
#include
#include
using namespace std;
void handler(int sig)
{
cout<<"recv sig:"<<sig<<endl;
sleep(5);
}
int main(void)
{
struct sigaction act;
act.sa_handler = handler;
sigemptyset(&act.sa_mask);
sigaddset(&act.sa_mask,SIGQUIT); //将SIGQUIT加入到掩码当中,(SIGQUIT被屏蔽了)
act.sa_flags = 0;
sigaction(SIGINT,&act,NULL); //注册SIGINT信号函数
while(1)
{
pause();
}
return 0;
}
测试结果:
./a.out
^Crecv sig:2
^^^^^^^^^^\退出 (核心已转储) //不能马上退出,因为SIGQUIT信号被屏蔽了
jiang@jiang-ThinkPad-X280:~/Cpp-Concurr
七)sigqueue函数的应用
sigqueue主要应用进程间通信!
//发送进程:
向一个进程发送SIGINT信号,并且将整型100也传进去!
#include
#include
#include
#include
#include
int main(int argc,char *argv[])
{
if(argc!=2)
{
printf("arguments error!");
exit(0);
}
pid_t pid=atoi(argv[1]);//将进程号转化为整数
union sigval v;
v.sival_int=100; //初始化sival_int的值
sigqueue(pid,SIGINT,v);//向pid发送SIGINT信号,并且传递额外信息v
return 0;
}
接收进程:
此进程等待接受信号的传入,并有SIGINT的信号处理函数!
#include
#include
#include
#include
#include
void handler(int,siginfo_t *,void *);
int main(void)
{
struct sigaction act;
act.sa_sigaction=handler;
sigemptyset(&act.sa_mask);
act.sa_flags=SA_SIGINFO; //必须填写SA_SIGINFO才可以进行进程间通信,传递数据
if(sigaction(SIGINT,&act,NULL)<0)
{
printf("error");
exit(0);
}
for(;;)
pause();
return 0;
}
void handler(int sig,siginfo_t * info,void *ctx)
{
printf("recv a sid=%d data=%d data=%d\n",sig,info->si_value.sival_int,info->si_int);//打印接收信息
}
测试结果如下:
终端执行命令: ./a.out 10728
打开另一终端:./recv
收到的数据是:recv a sid=2 data=100 data=100
八)信号的可靠性和不可靠性
发送进程:
发送三个可靠信号和三个不可靠信号,延时3妙后,发送恢复屏蔽信号,代码如下:
#include
#include
#include
#include
#include
int main(int argc,char *argv[])
{
if(argc!=2)
{
printf("arguments error!");
exit(0);
}
pid_t pid=atoi(argv[1]);//将进程号转化为整数
union sigval v;
v.sival_int=100; //初始化sival_int的值
sigqueue(pid,SIGINT,v);//向pid发送SIGINT信号,并且传递额外信息v
sigqueue(pid,SIGINT,v);//向pid发送SIGINT信号,并且传递额外信息v
sigqueue(pid,SIGINT,v);//向pid发送SIGINT信号,并且传递额外信息v
sigqueue(pid,SIGRTMIN,v);//向pid发送SIGINT信号,并且传递额外信息v
sigqueue(pid,SIGRTMIN,v);//向pid发送SIGINT信号,并且传递额外信息v
sigqueue(pid,SIGRTMIN,v);//向pid发送SIGINT信号,并且传递额外信息v
sleep(3);
kill(pid,SIGUSR1);
return 0;
}
接收进程:
设置一个可靠信号(信号排队),一个不靠信号,和一个屏蔽字恢复信号,使之解除阻塞,代码如下:
#include
#include
#include
#include
#include
void handler(int);
int main(void)
{
struct sigaction act;
act.sa_handler=handler;
sigemptyset(&act.sa_mask);
act.sa_flags=0;
sigset_t s;
sigemptyset(&s);
sigaddset(&s,SIGINT);
sigaddset(&s,SIGRTMIN);
sigprocmask(SIG_BLOCK,&s,NULL); //SIGINT或者SIGRTMIN信号到来的时候,就会被阻塞
sigaction(SIGINT,&act,NULL); //不可靠信号
sigaction(SIGRTMIN,&act,NULL);//可靠信号
sigaction(SIGUSR1,&act,NULL);//安装一个信号,解除阻塞
for(;;)
pause();
return 0;
}
void handler(int sig)
{
if(sig == SIGINT || sig == SIGRTMIN)
{
printf("recv a sid=%d\n",sig);//打印接收信息
}
else if(sig == SIGUSR1)
{
sigset_t s;
sigemptyset(&s);
sigaddset(&s,SIGINT);
sigaddset(&s,SIGRTMIN);
sigprocmask(SIG_UNBLOCK,&s,NULL);
}
}
测试结果如下:
接收进程运行结果:
./recv
recv a sid=34
recv a sid=34
recv a sid=34
recv a sid=2
发送进程运行结果:
./send 4739 //4739为接收进程号