信号的阻塞和未达

信号在内核中的表示

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

1)PCB进程控制块中有信号屏蔽状态字(block)信号未决状态字(pending)以及是否忽略标志;

2)信号屏蔽状态字(block),1代表阻塞、0代表不阻塞;信号未决状态字(pending)的1代表未决,0代表信号可以抵达了;

3)向进程发送SIGINT,内核首先判断信号屏蔽状态字是否阻塞,信号未决状态字(pending相应位写1,表示未决,不可抵达;若阻塞解除,信号未决状态字(pending)相应位制成0,表示信号可以抵达了。之后在抵达的时候再看是否忽略,采取相应的处理函数。

注意:只有当 向当前进程 发送的信号 发生 以后,信号未决状态字才会生效,此时如果信号屏蔽相应位是置1的,则信号未决位也置1,表示不可抵达,否则都是0,表示可抵达,至于表示其他信号的位,如果没有相应的信号发生,信号未决状态字并不能表明这些没有发生的信号是否可以抵达!

4)block状态字、pending状态字 64bit;

5)block状态字用户可以读写,pending状态字用户只能读;这是信号设计机制。

状态字都64bit,编程时,如何表示状态字那?
64bit/8=8个字节的缓冲区

信号集操作函数

    #include <signal.h>
    int sigemptyset(sigset_t *set); 把信号集清空,
    int sigfillset(sigset_t *set); 把信号集全部置成1
    int sigaddset(sigset_t *set, int signo); 根据signo,把信号集中的对应为置成1
    int sigdelset(sigset_t *set, int signo); 根据signo,把信号集中的对应为置成0
    int sigismember(const sigset_t *set, int signo);//判断signo是否在信号集中

sigprocmask函数

  • 功能:读取或更改进程的信号屏蔽状态字(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参数的可选值。
    这里写图片描述

说明:SIG_BLOCK ,讲信号集set添加到进程block状态字中。

注意: 进入信号处理函数时会将本信号加入全局信号屏蔽字,退出时还原,所以在信号处理函数里面对信号屏蔽字所做的操作只在该函数有效!一旦返回到main函数或者其他函数,系统将会恢复全局屏蔽字,也就是保留之前的屏蔽操作。意味着在信号处理函数里调用sigprocmask是不明智的选择,因为退出信号处理函数以后屏蔽字恢复成之前的状态(全局信号屏蔽字)。要修改屏蔽字,就需要在其它非信号处理函数调用sigprocmask修改当前进程的信号屏蔽字/集合。

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.

需要解决的问题

  • 信号未设置阻塞,查看未决关键字
  • 信号设置阻塞,查看未决关键字
  • 信号解除阻塞,查看未决关键字(通过ctrl+\ 或者SIGQUIT 解除阻塞)
  • 演示信号从产生到抵达的整个过程
  • 信号的阻塞和解除阻塞综合实验
  • 设置信号阻塞和非阻塞,设置ctl+q来解除信号阻塞


#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>


//演示信号从产生到抵达的整个过程

//设置信号阻塞和非阻塞,设置ctl+\来解除信号阻塞

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

int flag = 0;
void handler(int sig)
{
  sigset_t uset;

    if (sig == SIGINT)
    {
        printf("recv a sig=%d\n", sig);

    }
    else if (sig == SIGQUIT)
    {
    /*以下三句话是不能实现解除阻塞功能的!
    进入信号处理函数时会将本信号加入全局信号屏蔽字,
    退出时还原,所以在信号处理函数里面对信号屏蔽字所做的操作只在该函数有效!
    一旦返回到main函数或者其他函数,系统将会恢复全局屏蔽字
    也就是保留之前的屏蔽操作
    意味着在信号处理函数里调用sigprocmask是不明智的选择,
    因为退出信号处理函数以后屏蔽字恢复成之前的状态(全局信号屏蔽字)
    */
        sigemptyset(&uset);
        sigaddset(&uset, SIGINT);
        sigprocmask(SIG_UNBLOCK, &uset, NULL);

   //ctr + \ 用来解除 SIGINT 信号
        //解除阻塞
    flag = 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");
}

void change_mask()
{
    sigset_t uset;
    sigemptyset(&uset);
        sigaddset(&uset, SIGINT);
        sigprocmask(SIG_UNBLOCK, &uset, NULL);
}
int main(int argc, char *argv[])
{
    sigset_t pset; //用来打印的信号集
    sigset_t bset; //用来设置阻塞的信号集

    sigemptyset(&bset);
    sigaddset(&bset, SIGINT);

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

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

    //读取或更改进程的信号屏蔽字 这里用来阻塞ctrl+c信号
    //ctrl+c信号被设置成阻塞,即使用户按下ctl+c键盘,也不会抵达
    sigprocmask(SIG_BLOCK, &bset, NULL);

    for (;;)
    {
    if(1 == flag)
    {

      /*两种方法都可以修改当前进程的屏蔽状态字*/
      change_mask();//非信号处理函数修改
      //sigprocmask(SIG_UNBLOCK, &bset, NULL);//主函数修改
      flag = 0;
    }
        //获取未决 字信息
        sigpending(&pset);

        //打印信号未决  sigset_t字
        printsigset(&pset);
        sleep(1);
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值