【Linux】信号捕捉

1、内核如何实现信号的捕捉:

(1)如果信号的处理动作是用户自定义函数,在信号递达时就调用这个函数,这称为信号捕捉。

(2)范例:
用户程序注册了SIGQUIT信号的处理函数sighandler。当前正在执行main函数,这时发生中断或异常切换到内核态。在中断处理完毕后要返回用户态的main函数之前检查到有信号SIGQUIT递达。内核决定返回用户态后不是恢复main函数的上下文继续执行,而是执行sighandler函数,sighandler和main函数使用不同的堆栈空间,它们之间不存在调用和被调用的关系,是两个独立的控制流程。sighandler函数返回后自动执行特殊的系统调用sigreturn再次进入内核态。如果没有新的信号要递达,这次再返回用户态就是恢复main函数的上下文继续执行了。

(3)捕捉信号流程:
这里写图片描述

2、信号捕捉函数:

(1)signal函数:
注册一个信号捕捉函数:

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

该函数由ANSI定义,由于历史原因在不同版本的Unix和不同版本的Linux中可能有不同的行为。因此应该尽量避免使用它,取而代之使用sigaction函数。

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

void do_sig(int a)
{
    printf("Hi,SIGINT,how do you do!\n");
}

int main()
{   if(signal(SIGINT,do_sig)==SIG_ERR)
    {
        perror("signal");
        exit(1);
    }

    while(1)
    {
        printf("---------------\n");
        sleep(1);
    }

    return 0;
}
//每当捕捉到SIGINT信号就调用do_sig函数处理信号;

还可以使用以下方式捕捉信号:

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

typedef void (*sighandler_t)(int);

void catchsigint(int signo)
{ 
    printf(" handle signal!\n");
}
int main()
{
    sighandler_t handler;

    handler=signal(4,catchsigint);
    if(handler==SIG_ERR)
    {
        perror("signal\n");
        exit(1);
    }
    while(1)
    {
        printf("running...\n");
        sleep(1);
    }
        return 0;
}

(2)sigaction函数:
修改信号处理动作(通常在Linux用其来注册一个信号的捕捉函数)

#include<signal.h>

int sigaction(int signum,const struct sigaction* act,struct sigaction* oldact);
//成功返回0;  失败返回-1;

act: 传入参数,新的处理方式。
oldact: 传出参数,旧的处理方式。

struct 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_restorer:该元素是过时的,不应该使用,POSIX.1标准将不指定该元素。

sa_sigaction: 当 sa_flags被指定为SA_SIGINFO标志时,使用该信号处理程序。

sa_handler:指定信号捕捉后的处理函数名(即自定义函数)。也可赋值为SIG_IGN表忽略或SIG_DFL表执行默认动作。

sa_mask:调用信号处理函数时,所要屏蔽的信号集合(信号屏蔽字)。仅在处理函数被调用期间屏蔽生效,是临时性设置。

sa_flags:通常设置为0,表使用默认属性。

使用范例:

/*当执行SIGINT信号处理函数期间 
 *多次收到SIGQUIT信号都将被屏蔽(阻塞) 
 *SIGINT信号处理函数处理完,立刻解除对 
 *SIGQUIT信号的屏蔽,由于没有捕捉该信号, 
 *将立刻执行该信号的默认动作,程序退出 
 */ 
#include<stdio.h>
#include<stdlib.h>
#include<signal.h>
#include<unistd.h>

void sig_int(int signo)
{
    printf("catch signal SIGINT\n");
    sleep(10);
    printf("----wait 10 secons\n");   

}

int main()
{
    struct sigaction act;
    act.sa_handler=sig_int;
    act.sa_flags=0;
    sigemptyset(&act.sa_mask);    //初始化信号集,所有位置0,即不屏蔽任何信号
    sigaddset(&act.sa_mask,SIGQUIT);  //设置屏蔽字,屏蔽SIGQUIT信号

    sigaction(SIGINT,&act,NULL);

    printf("---------------wait signal\n");

    while(1);    //等待接收信号

    return 0;
}

猜你喜欢

转载自blog.csdn.net/lz201788/article/details/80044465