Quantcast
Channel: CSDN博客移动开发推荐文章
Viewing all articles
Browse latest Browse all 5930

信号捕捉

$
0
0

在前面的一篇文章中提到了关于信号的一些知识,可参考如下文章:

Linux中关于信号的一些知识


接下来在这篇文章中就谈一谈什仫是信号捕捉。
什仫是信号捕捉?
如果信号的处理动作是用户自定义函数,在信号递达时就调用这个函数,这称为捕捉信号。


与信号捕捉有关的函数操作:
1.读取/修改与指定信号相关联的处理动作,与signal函数类似;

#include <signal.h>
int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);

#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);

返回值:成功返回0,失败返回-1;
signum:指定信号的编号;
若act指针非空,则根据act修改该信号的处理动作;若oact指针非空,则通过oact传出该信号原来的处理动作。act和oact指向sigaction结构体:

struct sigaction {
      void     (*sa_handler)(int);
      void     (*sa_sigaction)(int, siginfo_t *, void *);
      sigset_t   sa_mask;
      int        sa_flags;
      void     (*sa_restorer)(void);
   };

sa_handler:早期的捕捉函数;
赋值为常数SIG_IGN传给sigaction表示忽略信号;赋值为常数SIG_DFL表示执行系统默认动作;赋值为一个函数指针表示用户自定义函数捕捉信号,或者说向内核注册了一个信号处理函数;
sa_sigaction:新添加的捕捉函数,通过sa_flags选择哪种捕捉函数;
新添加的捕捉函数,通过sa_flags选择哪种捕捉函数。
sa_mask:说明这些需要额外屏蔽的信号,当信号处理函数返回时自动恢复原来的信号屏蔽字;
sa_flags:包含一些选项,一般都将其设置为0;
sa_restorer:保留,已过期;
2.pause

#include <unistd.h>
int pause(void);

函数功能:pause函数使调用进程挂起直到有信号递达;
pause可能有以下几种情况:
1).如果信号的处理动作是终止进程,则进程终止,pause函数没有机会返回;
2).如果信号的处理动作是忽略,则进程继续处于挂起态,pause不返回;
3).如果信号的处理动作是捕捉,则调用了信号处理函数之后pause返回-1,errno设置为EINTR, 所以pause只有出错的返回值。
这个函数就有点类似进程的程序替换了。


信号捕捉时候的状态转化:
status
从上面这张图就可以看出信号捕捉时的状态转化:
1).当你遇到中断,异常或者系统调用的时候由用户态进入内核态
2).此时产生信号,由内核态切换到用户态,在这个过程中需要对pcb的那三张表进行检查,才会发现此时有递达的信号,此时就去去处理信号对应的操作,也就是信号处理函数;
3).处理信号处理函数的时候,这个时候为了安全的问题,这个时候为用户态;
4).信号处理函数结束后,然后从用户态切换到内核态
5.然后由内核态切换到中断异常执行处的用户态
所以通过以上的分析可知,在信号捕捉的状态转化过程中存在四次转化。


一个关于信号捕捉的简单的例子:
使用sigaction函数,pause函数以及alarm函数模拟sleep函数。

#include<stdio.h>
#include<signal.h>
#include<unistd.h>

void my_handler(int signo)
{
    //自定义的处理动作为空
}

int mysleep(int timeout)
{
    struct sigaction act,oact;
    act.sa_handler=my_handler;  //用户自定义的信号处理动作
    sigemptyset(&act.sa_mask);  //清空该信号量
    act.sa_flags=0;
    sigaction(SIGALRM,&act,&oact);  //注册闹钟信号处理函数
    alarm(timeout);  //设置闹钟
    pause();         //挂起   
    //有可能正常返回也有可能异常返回
    int ret=alarm(0);
    sigaction(SIGALRM,&oact,NULL);  //恢复信号之前的处理动作
    return ret;
}

int main()
{
    int count=0;
    while(1)
    {
        count++;
        mysleep(1);
        printf("mysleep %d\n",count);
    }
//  struct sigaction act,oact;
//  act.sa_handler=my_handler;  
//  sigemptyset(&act.sa_mask); 
//  act.sa_flags=0;
//  sigaction(2,&act,&oact);
//  while(1);
    return 0;
}

点击此处查看源码

这段代码有没有什仫问题呢?试想一下这样的情况:当我们执行完alarm之后在pause之前,别的进程会竞争夺走了CPU,夺走n秒后,SIGALRM递达了,然后n秒过后,这个时候就去执行pause,这样没有了SIGALRM这个信号,此时这个进程就无法在timeout时间内被唤醒就会一直被阻塞。
像这样由于时序问题而导致的错误,这就叫做竞态条件
而解决这个问题我们就需要让alarm和pause是一个原子操作,该进程在alarm之后无法被别的进程夺走CPU的使用权。
Linux中存在这样一个函数sigsuspend就提供了这样的操作。

#include <signal.h>
int sigsuspend(const sigset_t *mask);

返回值:类似pause函数没有成功时的返回值。sigsuspend没有成功返回值,只有执行了一个信号处理函数之后sigsuspend才返回,返回为-1,errno设置为EINTR;
mask:指定进程的信号屏蔽字,可以通过指定mask来临时解除对某个信号的屏蔽,然后挂起等待,当sigsuspend返回时,进程的信号屏蔽字恢复为原来的值,如果原来该信号是屏蔽的,从sigsuspend返回后依然是屏蔽的;
下面是我使用sigsuspend函数实现的mysleep函数:

#include<stdio.h>
#include<signal.h>
#include<unistd.h>

void my_handler(int signo)
{
    //自定义的处理动作为空
}

//int mysleep(int timeout)
//{
//  struct sigaction act,oact;
//  act.sa_handler=my_handler;  //用户自定义的信号处理动作
//  sigemptyset(&act.sa_mask);  //清空该信号量
//  act.sa_flags=0;
//  sigaction(SIGALRM,&act,&oact);  //注册闹钟信号处理函数
//  alarm(timeout);  //设置闹钟
//  pause();         //挂起   
//  //有可能正常返回也有可能异常返回
//  int ret=alarm(0);
//  sigaction(SIGALRM,&oact,NULL);  //恢复信号之前的处理动作
//  return ret;
//}

int mysleep(int timeout)
{
    struct sigaction act,oact;
    act.sa_handler=my_handler;  //用户自定义的信号处理动作
    sigemptyset(&act.sa_mask);  //清空该信号量
    act.sa_flags=0;
    sigaction(SIGALRM,&act,&oact);  //注册闹钟信号处理函数
    //使得alarm和pause成为原子操作
    sigset_t mask,omask,smask;
    sigemptyset(&mask);
    sigaddset(&mask,SIGALRM);
    sigprocmask(SIG_BLOCK,&mask,&omask);

    alarm(timeout);  //设置闹钟

    smask=omask;
    sigdelset(&smask,SIGALRM);
    sigsuspend(&smask);
    int ret=alarm(0);
    sigaction(SIGALRM,&oact,NULL);  //恢复信号之前的处理动作

    sigprocmask(SIG_SETMASK,&omask,NULL);
    return ret;
}

int main()
{
    int count=0;
    while(1)
    {
        count++;
        mysleep(1);
        printf("mysleep %d\n",count);
    }
//  struct sigaction act,oact;
//  act.sa_handler=my_handler;  
//  sigemptyset(&act.sa_mask); 
//  act.sa_flags=0;
//  sigaction(2,&act,&oact);
//  while(1);
    return 0;
}

在这里就分享结束了~~~

作者:ONEDAY_789 发表于2017/6/8 22:22:48 原文链接
阅读:8 评论:0 查看评论

Viewing all articles
Browse latest Browse all 5930

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>