信号阻塞

一:关于信号的几种状态
1:信号递达:实际执行信号的处理动作称为信号递达
2:信号未决:,从产生到递达之间的状态称为未决状态.进程可以选择阻塞某个信号,被阻塞的信号产生时将保持未决状态,直到进程解除对次信号的阻塞,才执行递达的动作.
注意,阻塞和忽略是不同的,只要信号被阻塞就不会被递达,而忽略是在递达之后可选的一种处理动作.
我们可以画图来表示信号在内核中的表示
每个信号都有两个标志位分别表示阻塞(block)和未决(pending),还有一个函数指针表示处理动作.信号产生时,内核在进程控制块中设置该信号的未决状态标志,直到信号递达才清除该标志.
1:SIGHUP信号未阻塞也未产生过,当它递达时执行默认处理动作
2:SIGINT信号产生过,但正在被阻塞,所以暂时不能抵达.虽然它的处理动作是忽略,但在没有接触阻塞之前不能忽略这个信号,因为进程仍有机会改变处理动作之后再接触阻塞
3:SIGQUIT信号未产生过,一旦产生SIGQUIT信号将被阻塞,它的处理动作是用户自定义函数sighandller
信号阻塞_第1张图片
block:阻塞信号集.前面我们了解位图的位可以表示有效状态.我们用位图的下标表示信号的编号,内容为0表示该信号非阻塞,为1表示该信号阻塞.
pending:未决信号集.也可以用位图表示.位图的下标表示编号.内容为1,表示收到该信号,为0表示没有收到该信号.
handler:递达.设置对应信号的处理方式:SIG_DFT表示默认,SIG_IGN:表示忽略
二:信号集操作函数
sigset_t 类型杜宇每种信号用哪个一个bit表示”有效”或”无效”状态,在内存中的存储我们可以想到用位图表示bit位的有效还是无效,这里我们主要研究信号集操作函数

#include
int sigemptyset(sigset_t *set);//初始化所指向的信号集,使其中所有的信号的对应bit清零,表示该信号不含任何有效信号
int sigfillset(sigset_t *set);//使其中所有信号的对应的bit置位,表示该信号集的有效信号
int sigaddset(sigset_t *set,int signo);//添加有效信号
int sigdelset(sigset_t *set,int signo);//删除有效信号
int sigismember(const sigset_t *set,int signo);//用于判断一个信号集的有效信号中是否包含某种信号,包含返回1,不包含返回0;

注意:在使用sigset_t类型的变量之前,一定要调用sigemptyset或者sigfillset做初始化;
三:sigprocmask
调用函数sigprocmask可以读取或更改进程的信号屏蔽字

#include
int sigprocmask(int how,const sigset_t *set,sigset_t *oset)

返回值:成功为1,失败为-1;
信号阻塞_第2张图片
如果oset是非空指针,则读取进程的当前信号屏蔽字通过oset参数输出.如果set是非空指针,则更改进程的信号屏蔽字,参数how指示如何更改.如果oset和set都是非空指针,则oset保存原来的信号屏蔽字,然后再根据set和how参数更改信号屏蔽字.
如果调用sigprocmask解除了当前肉签个未决的阻塞,则在sigprocmask返回前,至少有一个信号抵达.
四:信号未决
前面我们知道信号未决,是信号产生到递达之间的状态

#include
int sigpending(sigset_t *set);

sigpending读取 当前进程的未决信号集,通过set参数传出.调用成功则返回0,出错返回-1.
验证

#include
#include
#include
#include
void printfsigset(sigset_t *set)
{
    int i = 1;
    for(;i<32;i++)
    {
        if(sigismember(set,i))//判断当前信号是否在目标集合中
        {
            putchar('1');
        }
        else{
            putchar('0');
        }
    }
    puts("");
}
int main()
{
    sigset_t s,p;//定义信号对象

    sigemptyset(&s);//清空并且初始化
    sigaddset(&s,SIGINT);//添加信号
    sigprocmask(SIG_BLOCK,&s,NULL);//设置阻塞信号集.阻塞SIGINT信号
    while(1)
    {
        sigpending(&p) ;//获取未决信号集
        printfsigset(&p);
        sleep(1);
    }

    return 0;
}

信号阻塞_第3张图片
我们看到Ctrl-\仍然可以终止程序,因为SIGQUIT信号没有被阻塞.
我们再来验证信号的屏蔽与解除屏蔽

#include
#include
#include
#include

void printpending(sigset_t *set)
{
    int i = 1;
    for(;i<32;++i)
    {
      if(sigismember(set,i))//判断指定信号是否在目标信号中
        {
        putchar('1');
        }
     else{
            putchar('0');
        }
    }
    putchar('\n');
}
void handler(int sig)//获取信号
{
    printf("pid is %d\n",sig);
}
int main()
{
    sigset_t block,oldblock,pending;//定义阻塞信号集,旧的阻塞信号集,未决信号集
    sigemptyset(&block);//清空并且初始化
    sigemptyset(&oldblock);//清空并且初始化

    sigaddset(&block,2);//添加2号信号
    signal(2,handler);
    sigprocmask(SIG_SETMASK,&block,&oldblock);//将旧的阻塞信号集保存,将含有2号信号屏蔽
int count =0;
    while(1)
    {
        sigpending(&pending);//获取未决信号集
        printpending(&pending);
        sleep(1);

            if((count++) ==3)
            {
                printf("proc is recover\n");
                sigprocmask(SIG_SETMASK,&oldblock,NULL);
            }

    }
    return 0;
}

信号阻塞_第4张图片
五:信号捕捉:
如果信号的处理动作是用户自定义函数,在信号递达时就调用这个函数,称为捕捉信号:
我们举个栗子:
1:用户程序注册SIGQUIT信号的处理函数sighandler.
2:当前正在执行main()函数,这时发生中断或异常要切换到内核态执行异常
3:中断处理完后要返回用户态的main函数之前要检查到有信号SIGQUIT递达
4:内核决定返回用户态后不是恢复mian函数的上下文继续执行,而是执行sighandler函数,因为他们属于不同的堆栈空间,不存在调用与被调用的关系.
5:sighandler函数返回后自动执行特殊的系统调用sigreturn再次进入内核态
6:如果没有新的信号要抵达,这次再返回用户态就是恢复main函数的上下文继续执行.
我们用一张图来表示:
信号阻塞_第5张图片

你可能感兴趣的:(Linus,信号阻塞-信号屏蔽)