Fork me on GitHub

信号集

信号集

我们需要有一个能表示多个信号—信号集(signal set)的数据类型。我们将在sigprocmask类函数中使用这种数据类型,以便告诉内核不允许发生该信号集中的信号。

如前所述,不同的信号的编号可能超过一个整型变量所包含的位数,所有一般而言,不能用整型量中的一位代表一种信号,也就是不能用一个整型量表示信号集。POSIX.1定义数据类型sigset_t以包含一个信号集,并且定义了下列5个处理信号集的函数。

1
2
3
4
5
6
7
#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); //4个函数返回值:若成功,返回0,若出错,返回-1
int sigismember(const sigset_t *set,int signo); //返回值,若真,返回1,若假,返回0

函数sigemptyset初始化由set指向的信号集,清除其中所有信号。函数sigfillset初始化由set指向的信号集,使其包括所有信号。所有应用程序在使用信号集前,要对该信号集调用sigemptyset或sigfillset一次。这是因为C编译器将不赋初值的外部变量和静态变量都初始化为0,而这是否与给定系统上信号集的视线相对应并不清楚。

一旦已经初始化了一个信号集,以后就可在该信号集中增、删特定的信号。

实现

如果实现的信号数目少于一个整型量所包含的位数,则可用一位代表一个信号的方法实现信号集,例如,假定一种实现有31种信号和32位整型。sigemptyset函数将整型设置为0,sigfillset函数则将整型中的各位设置为1.

1
2
#define sigemptyset(ptr) (*(ptr) = 0)
#define sigfillset(ptr) (*(ptr) = ~(sigset_t)0,0)

注意,除了设置信号集中各位为1外,sigfillset必须返回0,所以使用c语言的逗号运算符,它将逗号运算符后的值作为表达式的值返回。

其他关闭某一位、开启某一位、检查某一位的操作类似bitmap算法操作。

函数sigprocmask

一个进程的信号屏蔽字规定了当前阻塞而不能递送给该进程的信号集。调用函数sigprocmask可以检测或更改,或同时进行检测和更改进程的信号屏蔽字。

1
2
3
#include <signal.h>
int sigprocmask(int how,const sigset_t *restrict set,sigset_t *restrict oset);
//返回值:若成功,返回0,若出错,返回-1

首选,若oset是非空指针,那么进程的当前信号屏蔽字通过oset返回。

其次,若set是一个非空指针,则参数how指示如何修改当前信号量屏蔽字。下图说明了how可选的值。SIG_BLOCK是或操作,而SIG_SETMASK则是赋值操作。注意,不能阻塞SIGKILL和SIGSTOP信号。

image

如果set是个空指针,则不改变该进程的信号屏蔽字,how的值也无意义。

在调用sigprocmask后如果由任何未决的、不在阻塞的信号,则在sigprocmask返回前,至少将其中之一递送给该进程。

函数sigpending

sigpending 函数返回一个信号集,对于调用进程而言。其中的各信号是阻塞不能递送的,因而也一定是当前未决的。该信号集通过set参数返回。

1
2
#include <signal.h>
int sigpending(sigset_t *set); //返回值:若成功,返回0,若出错,返回-1

示例

展示了前面的大部分函数和信号功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include "head.h"
static void sig_quit(int);
int main(void){
sigset_t newmask,oldmask,pendmask;
if(signal(SIGQUIT,sig_quit) == SIG_ERR){
printf("can't catch SIGQUIT\n");
}
// block SIGQUIT and save current signal mask
sigemptyset(&newmask);
sigaddset(&newmask,SIGQUIT);
if(sigprocmask(SIG_BLOCK,&newmask,&oldmask) < 0){
printf("SIG_BLOCK error\n");
}
sleep(5); //SIGQUIT here will remain pending
if(sigpending(&pendmask) < 0){
printf("sigpending error\n");
}
if(sigismember(&pendmask,SIGQUIT)){
printf("\nSIGQUIT pending\n");
}
// Restore signal mask which unblocks SIGQUIT
if(sigprocmask(SIG_SETMASK,&oldmask,NULL) < 0){
printf("SIG_SETMASK error\n");
}
printf("SIGQUIT unblocked\n");
sleep(5);
exit(0);
}
static void sig_quit(int signo){
printf("caught SIGQUIT\n");
if(signal(SIGQUIT,SIG_DFL) == SIG_ERR){ //restablished new handler
printf("can't reset SIGQUIT\n");
}
}

进程阻塞SIGQUIT信号,同时保存了当前信号屏蔽字(以便后面恢复),然后休眠5秒。在此期间,发送若干退出信号SIGQUIT,这些信号都被阻塞,不递送至该进程,休眠结束后,将SIGQUIT设置为不再阻塞,在sigprocmask设置为之前未阻塞的信号集返回前,之前阻塞的SIGQUIT被递送到进程,并执行相应的信号处理函数,这是我们发现,之前发送的多个SIGQUIT只执行了一次,说明信号在该系统中没有进行排队,然后重新注册新的SIGQUIT信号处理函数为默认,也就是终止进程,此时,进程调用sleep再此休眠5秒,再此期间,如果接收到SIGQUIT信号,则执行默认处理,终止进程。

1
2
3
4
5
6
7
8
9
10
[vrlive@iZ23chs2r19Z ten]$ ./sigprocmask.o
^\^\^\^\SIGQUIT pending
caught SIGQUIT
SIGQUIT unblocked
^\Quit
[vrlive@iZ23chs2r19Z ten]$ ./sigprocmask.o
^\SIGQUIT pending
caught SIGQUIT
SIGQUIT unblocked
^\Quit
-------------本文结束感谢您的阅读-------------

本文地址:http://www.wangxinri.cn/2018/05/18/信号集/
转载请注明出处,谢谢!

梦想夹带眼泪,咸咸的汗水!