linux 使用sigaction sigemptyset sigaddset函数

本文深入探讨了信号处理机制,包括信号的递达、未决状态及如何在线程间进行信号传递。通过具体示例介绍了如何使用信号处理函数以及线程如何响应这些信号。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

需包含头文件:“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(终止程序),默认处理(终止程序)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值