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\n");    
    }
    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一章的学习总结,错误之处还望指出。

posted on 2014-09-08 11:11  zkqian  阅读(587)  评论(0编辑  收藏  举报