学科分类
目录

信号阻塞

实时信号34~64号信号之所以是可靠信号,是因为其信号模型中存在一个信号队列,当一个信号递达,但进程正在处理其它信号时,该信号会被加入信号队列,等待进程处理。而1~31号信号中没有信号队列,若信号递达时进程正在处理其它信号,那么进程就会对该信号做忽略处理,也可以说该信号被丢弃。

对进程来说,信号的发送过于密集,即在处理信号的同时再次收到信号时,进程会将后到的信号丢弃。对于信号的发送方来说,应该发送的信号已经发送,自然不会再次发送;但对于作为信号接收方的进程来说,未对信号做出应有处理,这显然是不符合预期的。

信号屏蔽机制专门用于解决常规信号不可靠这一问题。在进程的PCB中,存在两个信号集,一个称为信号掩码(signal mask),另一个称为未决信号集(signal pending)。这两个信号集的实质都是位图,其中的每一位对应一个信号:若mask中某个信号对应的位被设置为1,信号会被屏蔽,进入阻塞状态;此时内核会修改pending中该信号对应的位为1,使该信号处于未决态,之后除非该信号被解除屏蔽,否则内核不会再向进程发送这个信号。

用户是不能直接操作未决信号集的,但可以在程序中以自定义的set位图,与mask进行位操作,以达到屏蔽或解除屏蔽的目的,从而进一步对pending造成影响。

1、 信号集设定函数

Linux系统中提供了一组函数,用于设定自定义信号集,这些函数都存在于函数库signal.h中,函数声明分别如下:

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);

这些函数中的参数set是一个sigset_t类型的指针,sigset_t是系统自定义类型,其实质是一个位图,一般用户是不会直接对位进行操作的,针对位的操作也由以上函数完成。

信号集设定函数的功能分别如下:

● sigempty()——将指定信号集清0;

● sigfillset()——将指定信号集置1;

● sigaddset()——将某个信号加入指定信号集;

● sigdelset()——将某个信号从信号集中删除;

● sigismember()——判断某个信号是否已被加入指定信号集。

2、 sigprocmask()函数

自定义位图设定完成后,要与mask位图进行位操作,以改变mask位图中的数据。Linux系统提供了一个用于位操作的函数——sigprocmask(),该函数位于函数库signal.h中,其函数声明如下:

int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);

函数sigprocmask()若调用成功则返回0,否则返回-1,并设置errno。该函数共有三个参数,参数set和oldset都是指向位图的指针:set是一个传入参数,一般指向用户自定义位图,该位图用于与mask位图进行位操作;oldset是一个传出参数,用于记录原mask位图的值。参数how用于设置位操作的方式,其取值分别如下:

● 当how设置为SIG_BLOCK时,set位图中记录需要屏蔽的信号,sigprocmask()函数相当于使mask与set进行位或操作,即:mask=mask|set;

● 当how设置为SIG_UNBLOCK时,set位图中记录需要解除屏蔽的信号,sigprockmask()函数相当于使mask按位与上set的取反,即:mask=mask&~set;

● 当how设置为SIG_SETMASK时,set位图表示用于替代mask的新屏蔽集,sigprocmask()函数的实际操作为:mask=set。

需要注意,系统中什么时候产生什么信号是有规律的,用户进程不应随便对mask进行修改,因此在用户进程中的功能实现之后,应尽量使用sigprocmask()的传出参数oldset恢复mask。

3、 sigpending()函数

在编程过程中,若想了解进程中信号的状态,可以使用sigpending()函数,该函数的功能是获取当前进程中未决信号集的信息,存在于函数库signal.h中,函数声明如下:

int sigpending(sigset_t *set);

sigpending()函数调用成功时返回1,否则返回-1,其参数set是一个传出参数,用户可设置一个位图传入该参数,来获取未决信号集信息。

案例4:以2号信号为例,通过位操作函数sigprocmask()与sigpending()函数函数获取信号状态。

 1    #include <stdio.h>
 2    #include <stdlib.h>
 3    #include <unistd.h>
 4    #include <string.h>
 5    #include <signal.h>
 6    void printset(sigset_t *ped)            //pending打印函数
 7    {
 8        int i;
 9        for(i=1;i<32;i++){
 10            if((sigismember(ped,i)==1))
 11                putchar('1');
 12            else
 13                putchar('0');
 14        }
 15        printf("\n");
 16    }
 17    int main()
 18    {
 19        sigset_t set,oldset,ped;            //信号集定义
 20        sigemptyset(&set);                    //初始化自定义信号集set
 21        sigaddset(&set,SIGINT);                //将2号信号SIGINT加入set
 22        sigprocmask(SIG_BLOCK,&set,&oldset);//位操作
 23        while(1){                            
 24            sigpending(&ped);
 25            printset(&ped);
 26            sleep(1);
 27        }
 28        return 0;
 29    }

编译案例,执行程序,终端会不断打印进程PCB中的未决信号集,初始情况下进程未决信号集中的每一位都应为0,因此打印的信息如下:

0000000000000000000000000000000

使用kill命令或组合按键Ctrl+C驱使内核发送信号SIGINT给当前进程,进程第一次接收到信号SIGINT后,sigprocmask()函数被触发,此后终端打印的信息如下:

0100000000000000000000000000000

之后继续向进程发送SIGINT信号,终端打印信息不变,说明信号SIGINT成功被屏蔽。

点击此处
隐藏目录