信号的相关概念

  • 信号的概念:信号机制是进程之间相互传递消息的一种方法,信号全称为软中断信号,也有人称作软中断。
    软中断信号用来通知进程发生了异步事件,进程之间可以互相通过系统调用kill发送软中断信号。内核也可以因为内部事件而给进程发送信号,通知进程发生了某个事件。注意,信号只是用来通知某进程发生了什么事件,并不给进程传递任何数据。
    收到信号的进程对各种信号有不同的处理方法。具体方法在下文会有详细的介绍。进程通过系统调用signal来指定进程对某个信号的处理行为。
  • 产生信号有以下4种方式:
    1.用户在终端按下某些键时,终端驱动程序会发送信号给前台进程,例如:crtl+c产生SIGINT信号,ctrl+\产生SIGQUIT信号,crtl+z产生SIGTSTP信号(可使前台进程停止)(一个命令后面加&可以放到后台运行)
    2.硬件异常产生信号,这些条件由硬件检测到并通知内核,然后内核向当前进程发送适当的信号。例如:当前进程执行了除以0的指令,CPU的运算单元会产生异常,内核将这个异常解释为SIGFPE信号发送给进程。再比如当进程访问了非法内存地址,MMU会产生异常,内核将这个异常解释为SIGSEGV信号发送给进程
    3.一个进程调用kill(2)函数可以发送信号给另一个进程。可以用kill(1)命令发送信号给某个进程,kill(1)命令也是调用kill(2)函数实现的,如果不明确指定信号则发送SIGTERM信号,该信号的默认处理动作是终止进程,当该内核检测到某种软件条件发生时也可以通过信号通知进程。例如:闹钟超时产生SIGALRM信号,向读端已关闭的管道写数据时产生SIGPIPE信号。如果不想按默认动作处理信号,用户程序可以调用sigaction(2)函数告诉内核如何处理某种信号。
    4.软件条件产生。
  • 处理信号的常见方式有以下3种(忽略、默认、自定义):
    1.忽略此信号
    2.执行该信号的默认处理动作(部分进程默认状态一般是终止进程)
    3.提供一个信号处理函数,要求内核在处理该信号时切换到用户态执行这个处理函数,这种方式称为捕捉一个信号
  • 信号其他相关常见概念:
    实际执行信号的处理动作称为信号递达(Delivery)
    信号从产生到递达之间的状态称为信号未决(Pending)
    进程可以选择阻塞(Block)某个信号
    被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作

    1.如果一个信号被Block一定不会被递达
    2.如果一个信号没有被Block,但被Pending但不会立即递达,而是在合适的时候
    这里写图片描述
    信号产生时,内核在进程控制块中设置该信号的未决标志,直到信号递达才清除该标志。
    SIGHUP信号未阻塞也未产生过,当它递达时执行默认处理动作。
    SIGINT信号产生过,但正在被阻塞,所以暂时不能递达。虽然它的处理动作是忽略,但在没有解除阻塞之前不能忽略这个信号,因为进程仍有机会改变处理动作之后再解除阻塞。SIGQUIT信号未产生过,一旦产生SIGQUIT信号将被阻塞,它的处理动作是用户自定义函数sighandler
  • 信号的几个缺点:
    1.系统开销太大
    2.发送信号的进程需要进行系统调用
    3.数量非常有限
    4.不能传送数据及参数
    首先介绍一个数据类型:
    sigset_t
    是位图类型,用户想要访问block表和pending表需要通过系统调用接口
    信号集操作函数
头文件:#include<signal.h>
int sigemptyset(sigset_t *set);设置为空
int sigfillset(sigset_t *set);填充
int sigaddset(sigset_t *set,int signo);添加
int sigdelset(sigset_t *set,int signo);删除
int sigismember(const sigset_t *set,int signo);判断是否在信号集中

前四个函数都是成功返回0,出错返回-1。
sigismember是一个布尔函数,用于判断一个信号集的有效信号中是否包含某种信号,若包含则返回1,不包含则返回0,出错返回-1。

  • 可以调用sigprocmask可以读取或更改进程的信号屏蔽字(阻塞信号集)
    阻塞信号集也叫作当前进程的信号屏蔽字,这里的屏蔽是阻塞而不是忽略
头文件:#include<signal.h>
函数原型:int sigpromask(int how,const sigset_t *set,sigset_t *oset);
返回值:若成功返回0;若出错则为1
参数:sigset_t -> 信号集
      第三个参数为输出型参数,目的是为了保存老的信号屏蔽字,若不想保存设置为NULL,就可以

SIG_BLOCK
set包含了我们希望添加到当前信号屏蔽字的信号,相当于mask = mask|set
SIG_UNBLOCK
set包含了我们希望从当前信号屏蔽字中解除阻塞的信号,相当于mask = mask&set
SIG_SETMASK
设置当前信号屏蔽字为set所指向的值,相当于mask = set
如果调用sigprocmask解除了对当前若干个未决信号的阻塞,则在sigprocmask返回前,至少将其中一个信号递达。

  • 可以调用sigpending函数来获取pengding信号集
头文件:#include<signal.h>
函数原型:int sigpending(sigset_t *set);
返回值:若成功返回0;若出错则为1
功能:读取当前进程的未决信号集,通过set参数传出
  • 下面是用刚学的几个函数做的实验:
#include <stdio.h>
#include<signal.h>
#include<unistd.h>
void printsigset(sigset_t *set)
{
    int i = 0;
    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);//相当于ctrl+c
    sigprocmask(SIG_BLOCK,&s,NULL);//设置阻塞信号集,阻塞SIGINT信号
    while(1)
    {
        sigpending(&p);//获取未决信号集
        printsigset(&p);
        sleep(1);
    }
    return 0;
}

运行结果:

这里写图片描述

程序运行时,每秒钟把各个信号的未决状态打印一遍,由于我们阻塞了SIGINT信号,按ctrl+c将会使SIGINT信号处于未决状态,但按ctrl+*仍然可以终止程序,因为*SIGQUIT信号没有阻塞。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章