需包含头文件:“signal.h”
认识结构体
信号的基本概念:
1.实际执行信号的处理动作(3种),称为信号递达;
2.信号从产生到递达之间(信号传递期间),称为信号未决;
当一个信号产生后,并不是立刻就到处理,中间需要传递(此时信号放在未决队列里),直到信号递达处理。
当一个信号被屏蔽后(阻塞),那么这个信号就不会被递达(一直为未决信号),直到该信号为非阻塞(不屏蔽),就会递达和处理。
信号忽略是递达后的一种处理方式,信号屏蔽是将变成阻塞信号,不能被递达。这两者是不同的。
不能被递达的信号,是不会影响程序运行的,因为都不会递达,也就不会执行处理该信号的动作。默认处理信号动作为终止程序,SIG_IGN(终止程序),自定义处理。
struct sigaction{
void (*sa_handler)(int); //是一个信号处理函数
void(*sa_sigaction)(int,siginfo_t*,void*); //同样也是信号处理函数
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void); //被废弃
}
说明:在某些系统中,两个信号处理函数实在一个联合体里,因此不要同时设置。
sa_mask成员用来处理信号函数在执行期间需要屏蔽的信号,特别是当某个信号被处理时,它会放入进程的信号掩码,因此信号处理函数执行期间这个信号不会再度发生
sa_flags成员指定信号处理的行为,决定使用某个信号处理函数(SA_AIGINFO使用sa_sigaction函数;0:使用sa_handler函数)
int sigaction(int signum,const struct sigaction* act,struct sigaction* oldact); 和signal函数一样可以相互替换。
捕获到signum信号,并作出处理
返回值0表示成功,-1表示失败
int sigemptyset(sigset_t* set);
初始化信号集,使信号集为空
int sigfillset(sigset_t* set);
把所有信号加入到集合中,linux支持64种信号
int sigaddset(sigset_t* set,int signum);
将制定的信号加入到信号集里
int sigdelset(sigset_t* set,int signum);
将制定的信号从信号集里删除
int sigismember(sigset_t* set,int signum);
查看制定的信号是否在信号集里
上述函数成功返回0,失败返回-1
int sigwait(sigset_t* set,int* sig);
函数说明:一直等,等待信号集里的信号发生,将发生的信号通过sig获得,并将信号从未决队列中删除,但是该信号依旧是阻塞(屏蔽的)的。
注意:sigwait等待的信号必须是调用线程中被屏蔽的(阻塞的),因为不是屏蔽的信号就会递达被处理,sigwait就会等不到这个信号了,会一直等。
个人理解:当信号被屏蔽后,就是阻塞信号。
当阻塞信号产生后就放在了未决队里里。
int pthread_sigmask(int how,const sigset_t* set,sigset_t* oldset);
使用该函数来屏蔽某个线程对某些信号的处理
how: SIG_BLOCK:将set信号集里的信号添加到阻塞信号里(屏蔽信号)
SIG_UNBLOCK:将set信号集里的信号从阻塞信号里删除
SIG_SETMASK:将set信号集里的信号设置为阻塞信号
oldset:获取旧的信号集
附:信号处理的第二个函数 typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
handler 有SIG_IGN(忽视该信号)此时,信号产生后会导致整个程序终止
SIG_DFL(默认处理)终止程序
sighandler_t 自定义处理,此时,信号产生后,会调用自定义处理函数,处理完后,程序会回到之前执行的地方继续执行。
与signal相同功能的sigaction函数也是一样,当信号产生后,调用处理函数,之后,会回到之前执行的地方继续执行。注意:sigaction函数处理信号时,会将该信号放到sa_msk里屏蔽掉,在该函数执行期间,不会有该信号产生,直到处理函数执行完。
注意: 当信号产生后,如果没有处理,那么程序(进程)就会终止。另外,信号是针对整个进程的。例如:信号SIGUSR1产生在thread1线程里,那么会在程序(整个进程)里找对该信号的处理,如果没有进行处理,那么程序终止。如果找到了对该信号的处理,那么,就在thread1线程里调用信号处理函数,处理后,继续执行后面的程序。对信号的处理函数(signal,sigaction),是针对整个进程的。调用注册的处理函数是,信号所产生的线程所调用的。
#include <signal.h>
#include <pthread.h>
#include <iostream>
#include <unistd.h>
using namespace std;
void sig(int num);
void* fun(void* p)
{
signal(13,sig);
while(1)
{ cout<<"11111111"<<endl;
cout<<pthread_self()<<endl;
sleep(1);
}
return NULL;
}
void* fun1(void*p)
{
while(1)
{
cout<<"22222222"<<endl;
sleep(1);
}
return NULL;
}
void sig(int num)
{
cout<<"aaaa= "<<pthread_self()<<endl;
}
int main()
{
pthread_t tid,tid1;
cout<<pthread_self()<<endl;;
pthread_create(&tid,NULL,fun,NULL);
pthread_create(&tid1,NULL,fun1,&tid);
sleep(1);
pthread_kill(tid1,13);
cout<<"join"<<endl;
pthread_join(tid,NULL);
pthread_join(tid1,NULL);
cout<<"aaa"<<endl;
return 0;
}
运行结果:3086772896
11111111
3086769056
22222222
join
11111111
3086769056
aaaa= 3076279200
22222222
11111111
3086769056
22222222
如上例子:线程tid里有信号13的处理函数,用signal注册。线程tid1里没有,但是往tid1里发送信号13后,此时,tid1线程会调用signal里注册的函数,对信号进行处理。接着会继续之前的地方继续执行。使用信号,和注册信号处理函数是对于整个进程的。信号产生后,会在整个进程里找寻该信号的处理函数。没有则终止,有就会有该产生信号的线程调用处理函数,然后继续执行。
例子2:
#include <iostream>
#include <signal.h>
#include<unistd.h>
using namespace std;
void ff(int a)
{
// while(1)
{
cout<<"ff: "<<pthread_self()<<endl;
sleep(1);
}
}
void* fun1(void* p)
{
cout<<"sub1: "<<pthread_self()<<endl;
sigset_t set;
sigaddset(&set,13);
int ss;
pthread_sigmask(SIG_UNBLOCK,&set,NULL);
sleep(1);
while(1)
{
cout<<"fun1"<<endl;
sleep(1);
}
return NULL;
}
void* fun(void* p)
{
cout<<"sub: "<<pthread_self()<<endl;
sigset_t set;
sigemptyset(&set);
sigaddset(&set,13);
int ss;
pthread_sigmask(SIG_BLOCK,&set,NULL);
sleep(1);
//sigwait(&set,&ss);
//cout<<ss<<endl;
while(1)
{
cout<<"fun"<<endl;
//pthread_sigmask(SIG_UNBLOCK,&set,NULL);
sleep(1);
}
cout<<"finish"<<endl;
return NULL;
}
int main()
{
struct sigaction sig,oldsig;
sig.sa_handler=ff;
sig.sa_flags = 0;
signal(13,ff);//或者sigaction(13,&sig,NULL);
pthread_t tid,tid2;
cout<<"main: "<<pthread_self()<<endl;
pthread_create(&tid,NULL,fun,NULL);
pthread_create(&tid2,NULL,fun1,NULL);
sleep(1);
pthread_kill(tid,13);
pthread_join(tid,NULL);
pthread_join(tid2,NULL);
cout<<"return "<<endl;
return 0;
}
运行结果:
main: 3078944464
sub: 3078937456
sub1: 3070544752
fun
ff: 3078937456
fun1
fun1
fun
fun1...
可以看出:tid线程先是屏蔽信号13,然后再取消屏蔽,tid2是不屏蔽信号13,。但主线程给线程tid发送信号13时,信号并没有立即别处理(因为线程tid屏蔽信号13,使它不能被递达,一直处于未决信号),等tid线程运行到,取消屏蔽信号13时,该信号就能递达,然后被处理了(自定义,默认是忽略)。
信号只有在递达才会被处理,自定义处理,还是SIG_IGN(终止程序),默认处理(终止程序)。