【APUE | 10】函数signal

函数signal

函数signal介绍

typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
signal函数
    作用1:站在应用程序的角度,注册一个信号处理函数
    作用2:忽略信号,设置信号默认处理 信号的安装和回复
参数
--signal是一个带signum和handler两个参数的函数,准备捕捉或屏蔽的信号由参数signum给出,接收到指定信号时将要调用的函数有handler给出
--handler这个函数必须有一个int类型的参数(即接收到的信号代码),它本身的类型是void
--handler也可以是下面两个特殊值:① SIG_IGN 屏蔽该信号        ② SIG_DFL 恢复默认行为

 SIGCHLD信号

通过 1 signal(SIGCHLD, SIG_IGN) 通知内核对子进程的结束不关心,由内核回收

  • 如果不想让父进程挂起,可以在父进程中加入一条语句:  signal(SIGCHLD, SIG_IGN); 表示父进程忽略SIGCHLD信号。
  • SIGCHLD信号:该信号是子进程退出的时候向父进程发送的。 子进程结束时, 父进程会收到这个信号。 

如果父进程没有处理这个信号,也没有等待(wait)子进程,子进程虽然终止,但是还会在内核进程表中占有表项,这时的子进程称为僵尸进程。这种情 况我们应该避免(父进程或者忽略SIGCHILD信号,或者捕捉它,或者wait它派生的子进程,或者父进程先终止,这时子进程的终止自动由init进程 来接管)。

1. 测试代码

 1 //忽略,屏蔽信号
 2 #include <stdio.h>
 3 #include <stdlib.h>
 4 #include <string.h>
 5 #include <unistd.h>
 6 #include <errno.h>
 7 #include <sys/types.h>
 8 #include <signal.h>
 9 
10 int main(int arg, char *args[])
11 {
12     pid_t pid=fork();
13     if(pid == -1)
14     {
15         printf("fork() failed! error message:%s
",strerror(errno));
16         return -1;
17     }
18     //注册信号,屏蔽SIGCHLD信号,子进程退出,将不会给父进程发送信号,因此也不会出现僵尸进程
19     signal(SIGCHLD,SIG_IGN);
20     if(pid > 0)
21     {
22         printf("father is runing !
");
23         sleep(10);
24     }
25     if(pid == 0)
26     {
27         printf("i am child!
");
28         exit(0);
29     }
30     printf("game over!
");
31     return 0;
32 }

2. 测试代码

 1 //恢复信号
 2 #include <stdio.h>
 3 #include <stdlib.h>
 4 #include <string.h>
 5 #include <unistd.h>
 6 #include <errno.h>
 7 #include <sys/types.h>
 8 #include <signal.h>
 9 
10 void catch_signal(int sign)
11 {
12     switch (sign)
13     {
14     case SIGINT:
15         printf("ctrl + C 被执行了!
");
16         //exit(0);
17         break;
18     }
19 }
20 
21 int main(int arg, char *args[])
22 {
23     //注册终端中断信号
24     signal(SIGINT, catch_signal);
25     char tempc = 0;
26     while ((tempc = getchar()) != 'a')
27     {
28         printf("tempc = %d
", tempc);
29         //sleep()
30     }
31     //恢复信号
32     signal(SIGINT, SIG_DFL);
33     while (1)
34     {
35         pause();
36     }
37     printf("game over!
");
38     return 0;
39 }

输出结果:

不可靠信号

什么是不可靠信号:不可靠的意思是信号可能丢失或者被错误处理。
在早起系统中,信号存在两大缺陷,导致了信号不可靠。
 

一、缺陷一:

信号发生后,信号处理方式被重置为系统默认动作。依旧是说,signal函数知识把信号和我们的信号处理函数关联一次,在发生一次信号后,信号的处理方式就被重置为系统默认了。
这就导致了信号处理函数必须使用如下代码:
 1 int  sig_int(); 
 2 ...
 3 signal(SIGINT, sig_int); /*①1establish handler*/    
 4 ...
 5 sig_int()
 6 {
 7 signal(SIGINT, sig_int); //②为了捕捉下一个信号 
 8 ...
 9 ./*process the signal ... */
10 ...
11 }
我们不得不在信号处理函数中再次使用signal()。
    但是,这样的处理并不能保证程序完全正确,因为在发生一次信号时,在我们开始调用sig_int函数,到执行sig_int函数中的signal函数(也就是我们②代码)之间是有时间间隔的.如果在这段时间间隔里发生了再次发生了信号,那么针对这个信号的处理方式就是系统默认的方法了。
    所以早期的信号时不可靠的,因为他不能保证信号都使用正确的(我们期望的)处理方式进行处理。

二、缺陷二:

信号对进程的控制能力差: 早期系统实现中,当我们不希望信号发生时,进程无法关闭一个 信号,并在记录它的发生。

很多时候我们有这样的需求,我们不希望信号打断某项的工作,但是当工作执行完后,又希望系统告诉我们这段时间内发生了什么信号。比如我们运行一段程序,要求运行完之前不能中断它(比如我们的Ctl+C),这是就需要暂时关闭这个信号。 首先我们明确需求,我们需要的是,信号暂时不起作用,并在之后能够提醒我们信号发生过。

 为了实现这一点,我们使用下面代码
 1 int  sig_int();     /* my signal handling function */
 2 int  sig_int_flag; /* set nonzero when signal occurs */
 3 main()
 4 {
 5     signal(SIGINT, sig_int); /* establish handler */
 6     ...
 7     while (sig_int_flag == 0)
 8         pause();  /* go to sleep, waiting for signal */
 9     ...
10 }
11 sig_int()
12 {
13     signal(SIGINT, sig_int); /* reestablish handler for next time */
14     sig_int_flag = 1; /* set flag for main loop to examine */
15 }

  sig_int只有两行代码,它的作用就是忽略信号,并且用sig_int_flag标志信号发生过。

可重入函数

使用可重入函数进行更安全的信号处理

 

1.测试代码

 1 #include<stdio.h>
 2 #include<signal.h>
 3 
 4 int value = 0;
 5 
 6 void fun() {
 7     int i = 0;
 8     while (i++<5) {
 9         value++;
10         printf("value is %d
", value);
11         sleep(1);
12     }
13 }
14 int main()
15 {
16     signal(2, fun);
17     fun();
18     printf("the value is %d
", value);
19     return 0;
20 }

输出结果:

参考资料

  1. APUE学习笔记——10 信号
原文地址:https://www.cnblogs.com/sunbines/p/9466605.html