前言
曾经的 UNIX 系统中,信号的不可靠的。什么是不可靠?就是信号丢失呗。那什么是信号丢失?就是当系统正在处理某个事务的时候,如果收到了某个信号,但它不能及时处理这个信号,那么只能忽略掉此信号。
而在可靠信号机制中,如果发生了上述情况,则要求系统处理完当前的事务后,还能够找回丢失的那个信号。也就是说,要具备本文即将讲述的 - 信号的阻塞功能。
信号的阻塞
系统在信号发生后,会在进程表中设置一个标志。在信号发生到进程表中设置一个标志的时间间隔之内,我们称信号是未决的。所谓信号阻塞,就是指将信号保持未决状态,直到用户将信号解除阻塞,该信号才被接收。
重要概念:信号集
信号集是一种数据类型,它能够表述系统内所有的信号:类似位图,某位为 1 表示该位对应的信号存在(被屏蔽),反之不存在(未被屏蔽)。对此数据类型的操作需要使用下表所提供的函数来进行:
大致步骤
1. 保存当前信号屏蔽集
2. 定义新的信号屏蔽集
3. 使新的信号屏蔽集生效
4. 恢复原来的信号屏蔽集
这四个步骤,所涉及到的只有一个函数:sigprocmask 函数,该函数的详细说明请参考相关文档。
代码实现
如下示例程序首先执行上述的步骤 1 2 3( 屏蔽退出信号 ),然后挂起 5 秒钟,然后在此期间产生 3 次退出信号,再执行上述步骤 4,最后再挂起 5 秒钟:
1 #include <stdio.h> 2 #include <unistd.h> 3 #include <signal.h> 4 5 // 信号处理函数( 此类函数都是无返回且形参为一个整数,该整数即为所接收到的信号 ) 6 static void sig_quit (int); 7 8 int main (void) { 9 10 // 定义信号集 11 sigset_t newmask, oldmask, pendmask; 12 13 // 注册退出信号处理函数 14 if (signal(SIGQUIT, sig_quit) == SIG_ERR) { 15 printf("注册信号处理函数失败 "); 16 return 1; 17 } 18 19 // 创建一个新的信号屏蔽字 20 sigemptyset(&newmask); 21 sigaddset(&newmask, SIGQUIT); 22 23 // 屏蔽退出信号,并将原来的信号屏蔽字保存起来。 24 if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0) { 25 printf("屏蔽退出信号失败 "); 26 return 2; 27 } 28 29 printf("已屏蔽退出信号 "); 30 31 sleep(5); 32 33 // 恢复原来的信号屏蔽字 34 printf(" 接下来恢复原来的信号屏蔽字 "); 35 if (sigprocmask(SIG_SETMASK, &oldmask, 0) < 0) { 36 printf("恢复信号屏蔽字失败 "); 37 return 2; 38 } 39 40 sleep(5); 41 42 return 0; 43 } 44 45 static void sig_quit (int signo) 46 { 47 printf("捕捉到退出信号 "); 48 49 // 将对退出信号的处理设置为关闭进程 50 if (signal(SIGQUIT, SIG_DFL) == SIG_ERR) { 51 printf("设置退出信号处理函数失败 "); 52 } 53 }
运行测试
第一次睡眠时,我发出了三次退出信号,结果只接收并处理了一次( 假如接收了两次,则第二次就会结束掉进程了。)。
当恢复到之前的信号屏蔽字以后,我再发出退出信号,进程立刻就关闭了。
小结
1. 可靠信号可靠之处在于可以使信号阻塞,到合适的时候再解除阻塞,接收信号。
2. 使用 sigaction 函数实现可靠信号更加方便。