13_信号的阻塞与未达

版权声明:原创,可以评论,技术交流,转载请标识转载何处,尊重作者 https://blog.csdn.net/WUZHU2017/article/details/81906517

信号在linux中的传递过程

信号在内核中的表示
   执行信号的处理动作称为信号递达(Delivery),信号从产生到递达之间的状态,称为信号未决(Pending)。进程可以选择阻塞(Block)某个信号。被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作。
   注意,阻塞和忽略是不同,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作。 

信号在内核中的表示可以看作是这样的:
这里写图片描述

说明1)PCB进程控制块中函数有信号屏蔽状态字(block),信号未决状态字(pending),还有是否忽略标志;
说明2)信号屏蔽状态字(block),1代表阻塞、0代表不阻塞;信号未决状态字(pending)的1代表未决,0代表信号可以抵达了;
说明3)向进程发送SIGINT,内核首先判断信号屏蔽状态字是否阻塞,信号未决状态字(若阻塞,pending相应位置1;若阻塞解除,信号未决状态字(pending)相应位置成0,此时表示信号可以抵达了。
说明4block状态字、pending状态字 64bit;
说明5block状态字用户可以读写,pending状态字用户只能读;这是信号设计机制。
说明6)可靠信号可以缓存n条, 不可靠信号只缓存1
    第一类 信号集操作函数(状态字表示)
             //清空一个信号集,即清除所有的信号
       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);

       block(阻塞)-->pending(未决)-->handle(处理)
       block:1阻塞; 0 非阻塞
       pending: 1 未决态 0可抵达
igprocmask读取或更改进程的信号屏蔽状态字(block) 
   #include <signal.h>int sigprocmask(int how, const sigset_t *set, sigset_t *oset);
   功能:读取或更改进程的信号屏蔽字。
   返回值:若成功则为0,若出错则为-1
   如果oset是非空指针,则读取进程的当前信号屏蔽字通过oset参数传出。如果set是非空指针,则更改进程的信号屏蔽字,参数how指示如何更改。如果oset和set都是非空指针,则先将原来的信号屏蔽字备份到oset里,然后根据set和how参数更改信号屏蔽字。假设当前的信号屏蔽字为mask,下表说明了how参数的可选值。

how含义

这里写图片描述

说明:SIG_BLOCK ,将信号集set添加到进程block状态字中
sigpending获取信号未决状态字(pending)信息 
NAME
       sigpending - examine pending signals
SYNOPSIS
       #include <signal.h>
       int sigpending(sigset_t *set);
DESCRIPTION
       sigpending()  returns the set of signals that are pending for delivery to the calling thread (i.e., the signals which have been raised while blocked).  The mask of pending signals is returned in set.
#include <signal.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>

#define ERR_EXIT(m) \
    do \
    { \
        perror(m); \
        exit(EXIT_FAILURE); \
    } while(0)

void handler(int sig)
{
    if (sig == SIGINT)
    {
        printf("recv a sig=%d\n", sig);
        printf("\n\n\n");
    }
    else if (sig == SIGQUIT)
    {
        sigset_t uset;
        //必须先清空信号集
        sigemptyset(&uset);
        //将SIGINT添加到集合中
        sigaddset(&uset, SIGINT);
        //ctr + \ 用来解除对  SIGINT 信号的阻塞,此时SIGINT对应的未决状态字响应位 是0,
        //表明信号可抵达
        //解除对SIGINT的阻塞
        sigprocmask(SIG_UNBLOCK, &uset, NULL);      
        //signal(SIGINT, SIG_DFL) ;//回复信号的默认处理动作
    }
}

//读取未决状态字 1未决  0表示可递达
//信号被阻塞时,其响应位的未决态置1 
void printsigset(sigset_t *set)
{
    int i;
    for (i=1; i<NSIG; ++i)
    {
        //判断信号是否在未决信号集中
        if (sigismember(set, i))
            putchar('1');
        else
            putchar('0');
    }
    printf("\n");
}
#if 1
//测试显示 信号未达状态 关键字
void test()
{
    sigset_t pset;
    for (;;)
    {
        //获取信号未决  sigset_t字
        sigpending(&pset); 
        //打印信号未决  sigset_t字
        //信号没有被阻塞,信号没有未决,所以没有东西
        printsigset(&pset);
        sleep(2);
    }
}
/*
    运行结果
    0000000000000000000000000000000000000000000000000000000000000000
    0000000000000000000000000000000000000000000000000000000000000000
    0000000000000000000000000000000000000000000000000000000000000000
^C

*/
#endif
//信号并没有阻塞,即使按下ctrl+c ,未决状态仍然不变
#if 1
void test()
{
    sigset_t pset;
    if (signal(SIGINT, handler) == SIG_ERR)
        ERR_EXIT("signal error");

    for (;;)
    {
        sigpending(&pset); 
        //信号没有被阻塞,信号没有未决,所以没有东西
        printsigset(&pset);
        sleep(2);
    }
}
/*
    运行结果:
    0000000000000000000000000000000000000000000000000000000000000000
    ^Crecv a sig=2



    0000000000000000000000000000000000000000000000000000000000000000
    c000000000000000000000000000000000000000000000000000000000000000
*/

#endif
/*
    1、信号不可靠,指的是信号可能会丢失
    2、信号没有优先级
    3、进程可以选用"阻塞信号递送", 只要进程没有对此信号解除阻塞,
       则该信号就是未决的!
    4、如果进程解除对信号的阻塞之前,这种信号发生了多次,对于POSIX.1
       允许系统递送该信号一次或者多次,若递送多次,则称这些信号进程了
       排队。
    5、如果多个信号要递送给一个进程,则对信号的递送顺序没有规定

 连续的按ctrl+c键盘,虽然发送了多个SIGINT信号,但是因为信号是不稳定的,只保留了一个。
//不支持排队
//PCB--》task_struct
*/
#if 1
void test()
{
    sigset_t pset; //用来打印的信号集 未决状态字
    sigset_t bset; //用来设置阻塞的信号集

    sigemptyset(&bset);//清空信号集
    sigaddset(&bset, SIGINT);//将2号信号SIGINT  添加到信号集中,并没有放入内核中

    if (signal(SIGINT, handler) == SIG_ERR)
        ERR_EXIT("signal error");

    if (signal(SIGQUIT, handler ) == SIG_ERR)//3号 SIGQUIT
        ERR_EXIT("signal error");

    //sigprocmask()读取或更改进程的信号屏蔽字,将信号集设置到内核中
    //这里用来阻塞ctrl+c信号
    //ctrl+c信号被设置成阻塞,即使用户按下ctl+c键盘,也不会抵达,不会调用信号处理函数
    sigprocmask(SIG_BLOCK, &bset, NULL);//每个进程都有PCB

    for (;;)
    {
        sigpending(&pset/*out*/);   
        //打印信号未决  sigset_t字
        printsigset(&pset);
        sleep(1);
    }
}
/*
    运行结果:
    开始没阻塞信号,打印的全是0
    0000000000000000000000000000000000000000000000000000000000000000
    0000000000000000000000000000000000000000000000000000000000000000
    当按下ctrl+c键,发一个信号,此信号被阻塞了,处于未决态
    ^C0100000000000000000000000000000000000000000000000000000000000000
    0100000000000000000000000000000000000000000000000000000000000000
    0100000000000000000000000000000000000000000000000000000000000000

    当按下ctrl+ \时,解除对SIGINT的阻塞,上次ctrl+c的信号刚抵达
    0100000000000000000000000000000000000000000000000000000000000000
    ^\recv a sig=2 


    //恢复了默认状态
    0000000000000000000000000000000000000000000000000000000000000000
    0000000000000000000000000000000000000000000000000000000000000000
    再按下crtl + c键
    0000000000000000000000000000000000000000000000000000000000000000
    ^C0100000000000000000000000000000000000000000000000000000000000000
    0100000000000000000000000000000000000000000000000000000000000000
    0100000000000000000000000000000000000000000000000000000000000000

*/
#endif
/*
    信号阻塞的产生与解除
*/
#if 1
 static void sig_quit()
 {
    printf(" sig_quit() caught SIGQUIT\n");
    //将SIGQUIT信号恢复默认
    if(signal(SIGQUIT, SIG_DFL) == SIG_ERR){
        perror("can't reset SIGQUIT\n");
    }
 }

void test(){
    sigset_t newmask, oldmask, pendmask;

    if(signal(SIGQUIT, sig_quit) == SIG_ERR){
        perror("can't reset SIGQUIT\n");
    }
    //清空信号集
    sigemptyset(&newmask);
    //将信号SIGQUIT加入到信号集中
    sigaddset(&newmask, SIGQUIT);
    //对信号SIGQUIT设置阻塞,设置到了内核中,将以前信号屏蔽字保存到oldmask
    if(sigprocmask(SIG_BLOCK, &newmask/*in*/, &oldmask/*out*/) < 0){
        perror("SIG_BLOCK error");
    }
    //休眠5s
    sleep(5);
    //获取未决信号状态
    if(sigpending(&pendmask) < 0)
    {
        perror("sigpending error");
    }
    //读取 SIGQUIT是否处于未决状态
    if(sigismember(&pendmask, SIGQUIT)){
        printf("\n SIG_QUIT pending\n");
    }

    #if 1//注释掉,信号不能递达
    //恢复以前信号屏蔽字的状态,最好不要用SIG_UNBLOCK,因为其他人也有可能对这个信号进行了设置
    if(sigprocmask(SIG_SETMASK, &oldmask, NULL)){
        perror("SIG_SETMASK error");
    }
    #endif
    printf("SIGQUIT unblock\n");
    //休眠5s
    sleep(5);
    exit(0);
}
/*
    运行结果:
    //第一次运行,只产生一次信号
    hzmct@U-64:$ ./dm01_sigstatus
    ^\
     SIG_QUIT pending
     sig_quit() caught SIGQUIT
    SIGQUIT unblock
    //第二次运行,产生多次信号
    hzmct@U-64:$ ./dm01_sigstatus
    ^\^\^\^\
     SIG_QUIT pending
     sig_quit() caught SIGQUIT
    SIGQUIT unblock

     第二次的运行结果表明,多次信号产生,只是向进程传送1次SIGQUIT,
     表明此系统没有将信号进行排队。

*/

#endif

int main(int argc, char *argv[])
{
    test();
    return 0;
}

猜你喜欢

转载自blog.csdn.net/WUZHU2017/article/details/81906517