zoukankan      html  css  js  c++  java
  • 【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 信号
  • 相关阅读:
    Linux配置YUM源(2020最新最详细)
    Linux系统安装Tomcat9(2020最新最详细)
    Linux系统安装JDK1.8(2020最新最详细)
    框架集项目-登录账户过期,如何回到登录主页!
    C# 以管理员方式运行程序
    C#/STM32 WAV转byte WAV数据格式
    C# base64 转 byte[]
    C# 截取屏幕图像
    emWin 学习笔记 —— 用VS2017打开emWin仿真包
    STM32 时钟配置的坑
  • 原文地址:https://www.cnblogs.com/sunbines/p/9466605.html
Copyright © 2011-2022 走看看