半生已过

导航

linux高级编程-信号(2)-信号处理函数的注册方式

信号处理函数的注册有两种方式,分别是简单版(signal)和高级版(sigaction)

简单版-signal():

  sighandler_t signal(int signum, sighandler_t handler);

    函数描述:signal() sets the disposition of the signal signum to handler,意思就是给信号设置处理函数

    参数signum:需要设置处理函数的信号

    参数handler:信号处理函数地址

    函数返回值:returns the previous value of the signal handler, or SIG_ERR on error,返回之前的处理方式,或返回错误

  函数的使用:

 1 /**
 2  * @brief handle : User-defined signal processing function
 3  * @param sig
 4  */
 5 void handle(int sig)
 6 {
 7     std::cout << " handle signal ******* " << sig << std::endl;
 8 }
 9 
10 int main(int argc, char *argv[])
11 {
12     // 注册自定义信号处理函数
13     if (SIG_ERR == signal(SIGINT, handle)) {
14         std::cout << " Failed to registered handle ******* " << SIGINT << std::endl;
15         return 0;
16     }
17 
18     // 将信号设置为默认处理方式
19     if (SIG_ERR == signal(SIGTERM, SIG_DFL)) {
20         std::cout << " Failed to registered handle ******* " << SIGTERM << std::endl;
21         return 0;
22     }
23 
24     for (;;) {sleep(1);}
25 }

  运行结果查看:

    

    通过运行结果查看,可以看出当按下 ctrl + c  的时候都会执行自定义信号处理函数     

高级版的使用-sigaction(): 

  int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);

    函数描述:

    参数signum:需要处理的信号

    参数act和参数oldact:If act is non-NULL, the new action for signal signum is installed from act.  If oldact is non-NULL, the previous action is saved in oldact     

      struct sigaction {

         void (*sa_handler)(int); //信号处理程序,不接受额外数据,SIG_IGN 为忽略,SIG_DFL 为默认动作

         void (*sa_sigaction)(int, siginfo_t *, void *); //信号处理程序,能够接受额外数据和sigqueue配合使用

         sigset_t sa_mask;//阻塞关键字的信号集,可以再调用捕捉函数之前,把信号添加到信号阻塞字,信号捕捉函数返回之前恢复为原先的值。

         int sa_flags;//影响信号的行为SA_SIGINFO表示能够接受数据

       };

  基本使用 ( 只用来注册信号处理函数,效果与signal()一样 ) 代码如下:  

 1 #include <iostream>
 2 #include <signal.h>
 3 #include <unistd.h>
 4 
 5 /**
 6  * @brief handle : User-defined signal processing function
 7  * @param sig
 8  */
 9 void handle(int sig)
10 {
11     for (int i = 0; i < 20; i++) {
12         std::cout << " handle signal ******* " << sig << std::endl;
13         sleep(1);
14     }
15 }
16 
17 int main(int argc, char *argv[])
18 {
19     struct sigaction act, oldact;
20     act.sa_handler = handle;
21     sigaction(SIGINT, &act, &oldact);
22 
23     for (;;) {sleep(1);}
24 }

   sigaction中sa_mask的使用:

    sa_mask的作用是用来设置在处理该信号时暂时将sa_mask 指定的信号搁置,也就是sa_mask指定的信号会被阻塞,直到sa_handler指向的处理函数执行完毕才会解除阻塞,举例说明:

    先看如下代码:

 1 #include <iostream>
 2 #include <signal.h>
 3 #include <unistd.h>
 4 
 5 void handleINT(int sig)
 6 {
 7     for (int i = 0; i < 20; i++) {
 8         std::cout << " handleINT ******* " << sig << std::endl;
 9         sleep(1);
10     }
11 }
12 
13 void handleTERM(int sig)
14 {
15     for (int i = 0; i < 5; i++) {
16         std::cout << " handleTERM ******* " << sig << std::endl;
17         sleep(1);
18     }
19 }
20 
21 int main(int argc, char *argv[])
22 {
23     struct sigaction act, oldact;
24     act.sa_handler = handleINT;
25     // sigaddset(&act.sa_mask, SIGTERM); // 用来设置在处理该信号时暂时将sa_mask 指定的信号搁置
26     sigaction(SIGINT, &act, &oldact);
27 
28     // registered SIGTERM
29     signal(SIGTERM, handleTERM);
30 
31     for (;;) {sleep(1);}
32 }

    代码的执行结果如下所示:

    

    在发送SIGINT信号之后发送SIGTERM信号(此时SIGINT信号的处理函数还在执行),我们发现SIGINT信号的处理函数被中止,而去执行SIGTERM的处理函数,等到SIGTERM的处理函数执行结束之后再回过头执行SIGINT的处理函数。

    如果我们使用了sa_mask了呢?,先看下方代码(注意与上面代码的不同之处):    

 1 #include <iostream>
 2 #include <signal.h>
 3 #include <unistd.h>
 4 
 5 void handleINT(int sig)
 6 {
 7     for (int i = 0; i < 20; i++) {
 8         std::cout << " handleINT ******* " << sig << std::endl;
 9         sleep(1);
10     }
11 }
12 
13 void handleTERM(int sig)
14 {
15     for (int i = 0; i < 5; i++) {
16         std::cout << " handleTERM ******* " << sig << std::endl;
17         sleep(1);
18     }
19 }
20 
21 int main(int argc, char *argv[])
22 {
23     struct sigaction act, oldact;
24     act.sa_handler = handleINT;
25     sigaddset(&act.sa_mask, SIGTERM); // 用来设置在处理该信号时暂时将sa_mask 指定的信号搁置
26     sigaction(SIGINT, &act, &oldact);
27 
28     // registered SIGTERM
29     signal(SIGTERM, handleTERM);
30 
31     for (;;) {sleep(1);}
32 }

    上面代码的执行效果如下所示,步骤与不使用sa_mask的一样

    

    使用sa_mask后,在发送SIGINT信号之后发送SIGTERM信号(此时SIGINT信号的处理函数还在执行),我们发现SIGINT信号的处理函数不会被中止,而SIGTERM的处理函数只有在SIGINT信号的处理函数执行结束之后才会执行。 也就是sa_mask指定的信号不会中断sa_handler函数。

  sigaction中sa_flags的使用:sa_flags的常用取值如下所示

取值 功能描述
SA_SIGINFO 说明了信号处理程序带有附加信息,也就是会调用 sa_sigaction 这个函数指针所指向的信号处理函数。在此,还要特别说明一下,sa_sigaction 和 sa_handler 使用的是同一块内存空间,相当于 union,所以只能设置其中的一个,不能两个都同时设置
SA_INTERRUPT 信号中断程序执行完毕之后,之前的系统调用会继续之心
SA_RESTART 信号中断程序执行完毕之后,之前的系统调用会继续之心
SA_NOCLDSTOP 一般当进程终止或停止时都会产生SIGCHLD信号,但若对SIGCHLD信号设置了该标志,则子进程停止时不产生此信号,终止时才产生此信号
SA_RESETHAND 当调用信号处理函数时,将信号的处理函数重置为缺省值SIG_DFL

  sigaction中sa_flags设置为SA_SIGINFO,也就是向信号处理函数中传递附加信息

    如果想将参数也传递到信号处理函数里面,需要用到sigqueue函数发送信号,代码如下图所示: 

 1 #include <iostream>
 2 #include <signal.h>
 3 #include <unistd.h>
 4 
 5 // 三参数信号处理函数的实现 **********************************
 6 void handle(int signum, siginfo_t *info, void *myact) //三参数信号处理函数的实现
 7 {
 8     std::cout << "signum ************* " << signum << std::endl;
 9     std::cout << "info **************** " << (char *)((*info).si_ptr) << std::endl;
10 }
11 
12 int main(int argc, char *argv[])
13 {
14     struct sigaction act, oldact;
15     act.sa_sigaction = handle; //三参数信号处理函数
16     act.sa_flags = SA_SIGINFO;
17     sigaction(SIGTERM, &act, &oldact);
18 
19     pid_t pid = getpid();
20     union sigval info;
21     char ch[] = "Info from handle";
22     info.sival_ptr = ch;
23 
24     for (;;) {
25         sigqueue(pid, SIGTERM, info); //向本进程发送信号,并传递附加信息
26         sleep(2);
27     }
28 }

    运行结果如下图所示:

      

  sa_flags其它取值使用不多暂不研究。

posted on 2021-04-15 20:24  半生已过  阅读(1265)  评论(0)    收藏  举报