• APUE学习之------------信号


           在学习一个东西的时候我总是喜欢去问这样做的理由是什么?也喜欢去究竟他的历史。从中你可以发现所有的设计都在不断改进出来的,从来就没有一个设计是一开始就是完美的。好比是人,之初,性也许是善的,如果我们不通过后天的学习去让自己的心灵完美的话,他就只停留在了人的初级阶段了。

          对于信号(signal)也是如此,硬件的中断中得出他的模型,然后不断的的去完善它。当一个事件发生时内核就会对该进程发送相应的信号,比如,内存错误了就是发送SIGSEGV,这个也就是我们常看到的段错误。再比如当我们做除零操作是就会发送SIGFPE。具体的信号定义的话可以参照http://www.cs.utah.edu/dept/old/texinfo/glibc-manual-0.02/library_21.html#SEC336 。下面就来看看具体的代码函数。

    1.信号的注册。

    typedef void (*sighandler_t)(int);
    
    sighandler_t signal(int signum, sighandler_t handler);

            这里不管你在做什么事情,产生一个软中断后内核deliver一个信号给进程,然后进程马上中止当前的任务去做信号处理相关的动作,但是我之前的事情也不能丢掉啊,等你做完信号处理的紧急的事情之后你应该继续把之前的时候完成 吧。于是乎就出现了可重入函数,等执行完信号处理函数之后,进程再次的回到之前的处理位置接着执行。关于那些事可重入的函数可以参考http://man7.org/linux/man-pages/man7/signal.7.html. 一个解决方案的引入通常带入了另外一个问题,如果这个函数是不可重入的呢?,在某一段代码中我们接收到信号,但是,我们又不想中断当前的函数?直接忽略这个型号吗?NO,操作系统又给我们提供了一些和信号集相关的函数。如果一段代码中你不想接受到某些信号你可以先将其阻挡在外,等处理完之后可以看下该型号是不是在block列表里,如果一个信号在被阻挡的列表了你可以对其单独的处理。这里的处理正的和生活中的某些处理很相似。看来我们的程序设计也是来源于生活的。下面使一些与之相关的函数:

    int sigemptyset(sigset_t *set);
    
    int sigfillset(sigset_t *set);
    
    int sigaddset(sigset_t *set, int signum);
    
    int sigdelset(sigset_t *set, int signum);
    
    int sigismember(const sigset_t *set, int signum);
    int sigpending(sigset_t *set);

    下面是一个简单samplecode

    #include <signal.h>
    #include <stdio.h>
    #include <string.h>
    #include <unistd.h>
     
    static int got_signal = 0;
     
    static void hdl (int sig)
    {
        got_signal = 1;
        printf("signal handle hd1");
    }
     
    int main (int argc, char *argv[])
    {
        sigset_t mask;
        sigset_t orig_mask;
     
        if (signal(SIGTERM,hdl)) {
            perror ("signal error");
            return 1;
        }
     
        sigemptyset (&mask);
        sigaddset (&mask, SIGTERM);
     
        if (sigprocmask(SIG_BLOCK, &mask, &orig_mask) < 0) {
            perror ("sigprocmask");
            return 1;
        }
     
        sleep (10);
         if (sigpending(&mask) == 0){
            printf("Signal TERM is pending
    ");    
        }
        if (sigprocmask(SIG_SETMASK, &orig_mask, NULL) < 0) {
            perror ("sigprocmask");
            return 1;
        }
     
        sleep (1);
     
        if (got_signal)
            puts ("Got signal");
     
        return 0;
    }

    一个函数的引入通常也引入了另一个问题,好像我的bug都是这样产生的,一个设计方案的导入通常会产生新的bug。现在有这样一个问题,我们希望变更一些信号处理函数,当前如果产生SIGA我们去做handA的操作,但是我们希望之后按照handB的操作去处理,最后又回到handA中又应该怎么处理呢?我们会使用以下的处理方式吗?

                         signal(SIGA, handA);

                         signal(SIGA, handB);

               signal(SIGA, handA);

    这种模式的缺点又在哪里呢?由于handA---->handB, handB--->handA的过程中不是原子操作,在handB--->handA的过程中产生一个SIGA信号会出现什么样的情况?这个信号岂不是丢失了。这里提供了两个信号处理函数无缝切换的函数。

    int sigaction(int signo, const struct sigaction *restrict act, struct sigaction *restrict oact);

    关于信号这块剩余的最后一个函数就是 sigsuspend了。我们先来看看他出现的理由吧!

    现在考虑一下的模型:

                        .

                        .

                       .

            sigprocmask(SIG_SETMASK, &oldmask, NULL);

           pause();

    以上的模型中如果在sigprocmask和pause之间发生了该信号,会发生什么事情?还没有执行pause函数,进程 接受到该信号后也不会被唤醒。最后导致该信号被丢失。导致信号丢失的原因在于sigprocmask和pause不是原子操作,在sigprocmask和pause之间有一个时间段,从而导致了信号丢失的发生。解决这个问题之要将sigprocmask和pause合并成要给原子操作就可以,于是就有了如下函数:

    int sigsuspend(const sigset_t *sigmask);

      以上是对APUEsignal一章的学习总结,错误之处还望指出。

  • 相关阅读:
    【c#.Net】类:面向对象
    【c#.Net】面试题库总结50题
    【c#.Net】C#面试题(.net开发人员必备)100题
    【C#.Net】方法和参数
    如何在Tomcat (6/7/8.0) 安装SSL证书
    ubuntu16.04安装jdk/mysql/tomcat (使用apt-get命令)
    java后台处理解析json字符串的两种方式
    web开发如何使用百度地图API(一)判断点是否在范围内
    ES6的let和var声明变量的区别
    web开发如何使用高德地图API(四)通过AMap.Marker自定义标点
  • 原文地址:https://www.cnblogs.com/lzh2nix/p/3961183.html
Copyright © 2020-2023  润新知