信号那点事(二)-信号集及相关操作

信号那点事(二)-信号集及相关操作

前言

    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


猜你喜欢

转载自blog.csdn.net/xiangguiwang/article/details/80266302