信号那点事(二)-信号集及相关操作
前言
POSIX.1 定义了一个数据类型sigset_t用于表示信号集。
使用信号集处理信号的方式与原来的信号方式不一样。主要体现在sigaction函数上。一旦对给定的的信号设置了一个动作,那么在调用sigaction显示的改变信号的处理方式之前,该设置就一直一样。
信号集操作
增加信号到信号集
int sigaddset(sigset_t *set, int signum);
addrespectively signal signum from set.
删处信号集中的信号
int sigdelset(sigset_t *set, int signum);
delete respectively signal signum from set.
查找信号集中的信号
int sigismember(const sigset_t *set, intsignum);
sigismember() tests whether signum is amember of set.
初始化信号集
int sigemptyset(sigset_t *set);
sigemptyset() initializes the signal setgiven by set to empty, with all signals excluded from the set.
int sigfillset(sigset_t *set);
sigfillset() initializes set to full,including all signals.
返回值
and return 0 on success and -1 on error.
基于信号集的应用
屏蔽信号
#include <signal.h>
int sigprocmask(int how, const sigset_t*set, sigset_t *oldset);
调用的行为取决于如何的值,如下所示。
SIG_BLOCK
阻塞信号的集合是进程当前信号集和set参数的并集。
SIG_UNBLOCK
set信号从当前进程被阻塞的信号中移除。允许尝试解除未被阻止的信号。
SIG_SETMASK
被阻塞的信号集被设置为set 。
sigprocmask函数用于获取或更改调用线程的信号掩码。信号掩码是当前调用线程希望阻止传送的一组信号。
如果oldset不是NULL,则设置新信号掩码之前的信号将存储在oldset中。
在多线程进程中使用pthread_sigmask。
查看当前进程或线程是否允许接收指定的信号
查看系统支持的信号
参考:https://blog.csdn.net/xiangguiwang/article/details/80148141
测试代码
#include <errno.h>
#include<stdio.h>
#include<signal.h>
#include<stdlib.h>
void pr_mask(intsig_num)
{
sigset_t sigset;
if (sigprocmask(0, NULL, &sigset)< 0) {
printf("sigprocmaskerror");
exit(0);
} else {
//返回0表示当前线程支持接收SIGINT信号。
if (sigismember(&sigset,sig_num)==0){
printf("signal %dexist \n",sig_num);
}else{
perror("error");
}
}
}
int main(){
pr_mask(77);
pr_mask(1);
pr_mask(64);
pr_mask(65);
return 0;
}
输出结果
error: Invalid argument
signal 1 exist
signal 64 exist
error: Invalid argument
查看是否有未决信号
SIGQUIT信号说明
当用户请求退出进程并执行核心转储时,SIGQUIT信号由其控制终端发送到进程。也就是说SIGQUIT信号会产生core dump文件。
测试代码
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
static void sig_quit(int);
int
main(void)
{
sigset_t newmask, oldmask, pendmask;
if(signal(SIGQUIT, sig_quit) == SIG_ERR)
printf("can'tcatch SIGQUIT");
/*
* Block SIGQUIT and save current signal mask.
*/
sigemptyset(&newmask);
sigaddset(&newmask,SIGQUIT);
if(sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0){
perror("SIG_BLOCK error");
}
sleep(5); /* SIGQUIT here will remain pending */
if(sigpending(&pendmask) < 0)
perror("sigpending error");
if(sigismember(&pendmask, SIGQUIT))
printf("\nSIGQUITpending\n");
/*
* Restore signal mask which unblocks SIGQUIT.
*/
if(sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
perror("SIG_SETMASK error");
printf("SIGQUITunblocked\n");
sleep(5); /* SIGQUIT here will terminate with corefile */
exit(0);
}
static void
sig_quit(int signo)
{
printf("caughtSIGQUIT\n");
if(signal(SIGQUIT, SIG_DFL) == SIG_ERR)
perror("can't reset SIGQUIT");
}
测试结果
^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\
SIGQUIT pending
caught SIGQUIT
SIGQUIT unblocked
^\Quit (core dumped)
从改测试用例可以看出来,信号在centos上是不会排队处理的。程序接收多个信号只会处理一次。
sigaction指定信号相关联的处理动作
int sigaction(int signum, const struct sigaction *act, structsigaction *oldact);
sigaction系统调用用于在接收到特定信号时更改进程所采取的操作。
signum指定信号,可以是除SIGKILL和SIGSTOP以外的任何有效信号。
如果act为非NULL,则从act中设置信号signum的新操作。如果oldact为非NULL,则先前的操作将保存为oldact。
测试代码
#include<setjmp.h>
#include<time.h>
#include<stdlib.h>
#include<stdio.h>
#include<errno.h>
#include<signal.h>
#include<unistd.h>
static void sig_usr1(int);
static void sig_alrm(int);
int main(void)
{
struct sigaction act, oact;
act.sa_handler = sig_usr1;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
if(sigaction(SIGUSR1, &act,&oact) < 0)
return 0;
struct sigaction act1, oact1;
act1.sa_handler = sig_alrm;
sigemptyset(&act1.sa_mask);
act1.sa_flags = 0;
#ifdef SA_INTERRUPT
act1.sa_flags |= SA_INTERRUPT;
#endif
act1.sa_flags |= SA_RESTART;
if(sigaction(SIGALRM, &act1,&oact1) < 0)
return 0;
for ( ; ; )
pause();
}
static void
sig_usr1(intsigno)
{
printf("catch USR1\n");
alarm(3); /*SIGALRM in 3 seconds */
}
static void
sig_alrm(intsigno)
{
printf("in sig_alrm: \n");
}
测试结果
./test &
ps -ef |grep mask | grep -v"grep" | awk -F ' ' '{print $2}' | xargs kill -USR1
从测试结果可以看出来sigaction函数取代看signal函数。
信号处理中的非局部跳转
int sigsetjmp(sigjmp_buf env, intsavesigs);
void siglongjmp(sigjmp_buf env, int val);
sigsetjmp()与setjmp()类似。 如果且只有当savingigs非零时,进程的当前信号掩码才会保存在env中,并且如果稍后调用siglongjmp它将被恢复。
测试代码
#include <setjmp.h>
#include<time.h>
#include<stdlib.h>
#include<stdio.h>
#include<errno.h>
#include<signal.h>
#include<unistd.h>
static void sig_usr1(int);
static void sig_alrm(int);
static sigjmp_buf jmpbuf;
/*提供一种保护机制,防止在未使用sigsetjmp初始化jmpbuf ,就调用USR1信号的处理函数*/
static volatilesig_atomic_t canjump;
void pr_mask(constchar *str)
{
sigset_t sigset;
int errno_save;
errno_save = errno;
/* we can be called by signal handlers*/
if (sigprocmask(0, NULL, &sigset)< 0) {
printf("sigprocmaskerror");
exit(0);
} else {
printf("%s", str);
if (sigismember(&sigset,SIGUSR1)==0){
printf(" SIGUSR1not shield \n");
}else{
printf("SIGUSR1 shield \n");
}
if (sigismember(&sigset, SIGALRM)==0){
printf(" SIGALRMnot shield \n");
}else{
printf("SIGALRM shield \n");
}
printf("\n");
}
errno = errno_save; /* restore errno */
}
int main(void)
{
if (signal(SIGUSR1, sig_usr1) ==SIG_ERR){
printf("signal(SIGUSR1)error\n");
return 0;
}
if (signal(SIGALRM, sig_alrm) ==SIG_ERR){
printf("signal(SIGALRM)error");
return 0;
}
pr_mask("starting main: "); /* {Prog prmask} */
if (sigsetjmp(jmpbuf, 1)) {
pr_mask("ending main:");
exit(0);
}
canjump = 1; /* now sigsetjmp() is OK */
for ( ; ; )
pause();
}
static void
sig_usr1(intsigno)
{
time_t starttime;
if (canjump == 0){
printf("%s\n","catch and not deal !!!");
return; /* unexpected signal, ignore */
}
pr_mask("starting sig_usr1: ");
alarm(3); /*SIGALRM in 3 seconds */
starttime = time(NULL);
for ( ; ; ) /* busy wait for 5 seconds */
if (time(NULL) > starttime + 5)
break;
pr_mask("finishing sig_usr1:");
canjump = 0;
siglongjmp(jmpbuf, 1); /* jump back to main, don't return */
}
static void
sig_alrm(intsigno)
{
pr_mask("in sig_alrm: ");
}
测试结果
ps -ef |grep mask | grep -v"grep" | awk -F ' ' '{print $2}' | xargs kill -USR1
starting main: SIGUSR1 not shield
SIGALRM not shield
starting sig_usr1: SIGUSR1 shield
SIGALRM not shield
in sig_alrm: SIGUSR1 shield
SIGALRM shield
finishing sig_usr1: SIGUSR1 shield
SIGALRM not shield
ending main: SIGUSR1 not shield
SIGALRM not shield
从上述测试结果可以看出,在调用sigsetjmp后,USR1信号的处理函数中USR1 信号被屏蔽。在调用siglongjmp之前,ALRM信号处理函数中ALRM、USR1信号同时被屏蔽。
参考资料
[1].https://en.wikipedia.org/wiki/Signal_(IPC)
[2].《Unix 环境高级编程》
[3]. http://man7.org/linux/man-pages/man3/errno.3.html
[4].https://blog.csdn.net/xiangguiwang/article/details/68313136
[5].http://www.man7.org/linux/man-pages/man2/sigaction.2.html