|
一、信号集
sigset_t 类型其实是个结构体,定义在 /usr/include/bits/sigset.h 头文件中:
[C++] 纯文本查看 复制代码 typedef __sigset_t sigset_t
typedef struct {
unsigned long int __val[ _SIGSET_NWORDS ];
}sigset_t;
而 _SIGSET_NWORDS 定义为:
[Plain Text] 纯文本查看 复制代码 # define _SIGSET_NWORDS (1024 / (8 * sizeof (unsigned long int)))
因此,_SIGSET_NWORS 的值为 32 。
POSIX 还定义了一系列函数用来操作信号集。在 shell 下输入 man sigsetops 可查看她们的函数原型如下:
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);
各个函数的含义为:
sigemptyset : 用来初始化一个信号集,使其不包括任何信号。 sigfillset : 用来初始化一个信号集,使其保留所有信号。 sigaddset : 用来向 set 指定的信号集中添加由 signum 指定的信号。 sigdelset : 用来从 set 指定的信号集中删除由 signum 指定的信号。 sigismember : 用来测试信号 signum 是否包括在 set 指定的信号集中。 sigemptyset(), sigfillset(), sigaddset(), sigdelset() 这几个函数在执行成功时返回 0 ,失败返回 -1。函数 sigismember() 返回 1 表示测试的信号在信号集中,返回 0 表示测试的信号不在信号集中,出错返回 -1 。
注意:所有应用程序在使用信号集前,要对该信号集调用一次 sigemptyset() 或 sigfillset() 以初始化信号集。这是因为 C 语言编译器将不赋初值的外部和静态量都初始化为 0 。
二、信号屏蔽
信号屏蔽又成为信号阻塞,在 shell 下输入 man sigprocmask 可获得信号阻塞的一系列函数的说明:
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
int sigpending(sigset_t *set);
int sigsuspend(const sigset_t *mask);
( 1 )sigprocmask() 函数
每个进程都有一个信号屏蔽码,它规定了当前阻塞而不能递送给该进程的信号集。sigprocmask() 可以检测或更改进程的信号屏蔽码。如果 oldset 是非空指针,则该进程之前的信号屏蔽码通过 oldset 返回(相当于一个备份);如果 set 为非空指针,则函数根据参数 how 来修改信号当前的屏蔽码,how 的取值为:
SIG_BLOCK : 将进程新的信号屏蔽码设置为当前信号屏蔽码和 set 指向信号集的并集。 SIG_UNBLOCK : 将进程的信号屏蔽码设置为当前信号屏蔽码中删除了 set 所指向信号集,即 set 包含了我们希望解除阻塞的信号。即使对当前信号屏蔽码中不存在的信号使用 SIG_UNBLOCK 也是合法操作。 SIG_SETMASK : 将进程新的信号屏蔽码设置为 set 所指向的值。 函数执行成功返回 0,有错误时返回 -1,错误代码存入 errno 中。
( 2 )sigpending() 函数
函数 sigpending() 用来获取调用进程因被阻塞而不能递送和当前未决的信号集。该信号通过参数 set 返回。函数执行成功返回 0 ,有错误返回 -1,错误代码存入 errno 中。
测试代码:
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
void my_err(const char *err_string, int line)
{
fprintf(stderr, "line:%d", line);
perror(err_string);
exit(1);
}
void hander_sigint(int signo)
{
printf("recv SIGINT\n");
}
int main()
{
sigset_t newmask, oldmask, pendmask; //定义信号集
/*安装信号处理函数*/
if (signal(SIGINT, hander_sigint) == SIG_ERR) {
my_err("signal", __LINE__); /*__LINE__ 表示当前__LINE__ 所在的行数*/
}
printf("进入睡眠10秒\n");
sleep(10);
printf("睡眠醒来\n");
/*初始化信号集 newmask 并将 SIGINT 添加进去*/
sigemptyset(&newmask);
sigaddset(&newmask, SIGINT);
/*屏蔽信号 SIGINT*/
if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0) {
my_err("sigprocmask", __LINE__);
} else {
printf("SIGINT blocked\n");
}
sleep(10);
/*获取未决信号队列*/
if (sigpending(&pendmask) < 0) {
my_err("sigpending", __LINE__);
}
/*检查未决信号队列里是否有 SIGINT*/
switch (sigismember(&pendmask, SIGINT)) {
case 0:
printf("SIGINT is not in pending queue\n");
case 1:
printf("SIGINT is in pending queue\n");
break;
case -1:
my_err("sigismember", __LINE__);
break;
default:
break;
}
/*解除对 SIGINT 的屏蔽*/
if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) {
my_err("sigprocmask", __LINE__);
} else {
printf("SIGINT unblocked\n");
}
while(1)
;
return 0;
}
运行及输出:beyes@linux-beyes:~/C> ./sig_mask.exe
进入睡眠10秒
^Crecv SIGINT
睡眠醒来
SIGINT blocked
^C^C^C^C^C^C^C^C^CSIGINT is in pending queue
recv SIGINT
SIGINT unblocked
^\退出 说明:
在输出结果中,^C 表示按下了一次 Ctrl + C 的组合键。由输出结果可以分析得:
在程序进入睡眠时(10s),只要接收到信号,它就会马上醒过来,然后进行接收到信号处理,如上面的输出:recv SIGINT。注意到,“睡眠醒来“ 醒来这句话是在 “recv SIGINT” 之后的,这是因为程序醒来后,第一件事是要去处理信号,而并不着急要输出睡眠函数的下面的语句。接着,我们阻塞了 SIGINT 信号,然后又进入 10 秒的睡眠。这时,连续多次输入 ctrl+c 后程序并不会就立马响应这些信号。所以,当用 sigismember() 函数来测试 SIGINT 是否在未决信号队列中,由输出 SIGINT blocked 可见,SIGINT 信号确实已经被阻塞。再接着,我们解除了 SIGINT 信号的屏蔽,用的是 sigprocmask(SIG_SETMASK, &oldmask, NULL),这里 oldmask 是原来屏蔽的信号集(相当于在设置新的信号集前我们对之前的信号集做了一个备份)。当 SIGINT 信号的屏蔽被解除后,程序马上去处理这个信号。再注意到,SIGINT unblocked 提示后于 recv SIGINT 输出,也是因为一旦解除了被屏蔽的信号且未决信号队列中有这个信号,那么程序会立即处理信号函数,而不着急输出提示。为什么只有一次 recv SIGINT 输出呢?这是因为 SIGINT 信号是不可靠信号,当有多个这样的信号时,信号处理函数往往只会被调用一次。
下帖会用一个可靠的实时信号了做一个测试。 |
|