Linux 改进捕捉信号机制(sigaction,sigqueue)

时间:2021-01-15 14:45:06
sigaction函数
sigaction函数的功能是用于改变进程接收到特定信号后的行为。
int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);
参数
--第一个参数是信号的值,可以为除了SIGKILL及SIGSTOP外的任何一个特定有效的信号(因为这两个信号定义了自己的处理函数,将导致信号安装错误)
--第二个参数是指向节后sigaction的一个实例的指针,在sigaction的实例中,指定了对特定信号的处理,可以为NULL,进程会以缺省方式对信号处理
--第三个参数oldact指向的对象用来保存原来对相应信号的处理,可以为NULL
返回值:函数成功返回0,失败返回-1

 

sigaction函数检查或修改与指定信号相关联的处理动作,该函数取代了signal函数。
因为signal函数在信号未决时接收信号可能出现问题,所以使用sigaction更安全。

 

sigaction结构体
struct sigaction {
    void     (*sa_handler)(int);//信号处理程序 不接受额外数据
    void     (*sa_sigaction)(int, siginfo_t *, void *);//信号处理程序,能接受额外数据,可以和sigqueue配合使用
    sigset_t   sa_mask;
    int        sa_flags;//影响信号的行为SA_SIGINFO表示能接受数据
    void     (*sa_restorer)(void);//废弃
};
--第二个参数最为重要,其中包含了对指定信号的处理、信号所传递的信息、信号处理函数执行过程中赢屏蔽掉哪些函数等等。
--回调函数sa_handler、sa_sigaction只能任选其一

 

//捕捉信号
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>

void catch_signal(int sign)
{
    switch(sign)
    {
    case SIGINT:
        //SIGINT默认行为是退出进程
        printf("SIGINT signal\n");
        exit(0);
        break;
    case SIGALRM:
        //SIGALRM默认行为是退出进程
        printf("SIGALRM signal\n");
        break;
    case SIGKILL:
        printf("SIGKILL signal\n");
        break;
    }
}

//建议使用封装之后的mysignal
int mysignal(int sign,void (*func)(int))
{
    struct sigaction act,oact;
    //传入回调函数
    act.sa_handler=func;
    //将act的属性sa_mask设置一个初始值
    sigemptyset(&act.sa_mask);
    act.sa_flags=0;
    return sigaction(sign,&act,&oact);
}

int main(int arg, char *args[])
{
    mysignal(SIGINT,catch_signal);
    mysignal(SIGALRM,catch_signal);
    mysignal(SIGKILL,catch_signal);
    int i=0;
    while(1)
    {
        printf("hello god  %d\n",i++);
        sleep(1);
    }
    return 0;
}

 

sigqueue函数
--新的发送信号系统调用,主要是针对实时信号提出的支持信号带有参数,与sigaction()函数配合使用
--注意:和kill函数相比int kill(pid_t pid,int signo)多了参数
--原型    int sigqueue(pid_t pid,int signo,const union sigval value);
--参数    sigqueue的第一个参数是指定接收信号的进程pid,第二个参数确定即将发送的信号,
第三个参数是一个联合数据结构union sigval,指定了信号传递的参数,即通常所说的4字节值。
--函数成功返回0,失败返回-1,并且更新errno --sigqueue()比kill()传递了更多的附加信息,但sigqueue()只能向一个进程发送信号,而不能发送信号给一个进程组 --union sigval联合体 typedef union sigval { int sival_int; void * sival_ptr; }sigval_t;

 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
/*
 siginfo_t {
               int      si_signo;    // Signal number
               int      si_errno;    // An errno value
               int      si_code;     // Signal code
               int      si_trapno;   // Trap number that caused
                                        hardware-generated signal
                                        (unused on most architectures)
               pid_t    si_pid;      // Sending process ID
               uid_t    si_uid;      // Real user ID of sending process
               int      si_status;   // Exit value or signal
               clock_t  si_utime;    // User time consumed
               clock_t  si_stime;    // System time consumed
               sigval_t si_value;    // Signal value
               int      si_int;      // POSIX.1b signal
               void    *si_ptr;      // POSIX.1b signal
               int      si_overrun;  // Timer overrun count; POSIX.1b timers
               int      si_timerid;  // Timer ID; POSIX.1b timers
               void    *si_addr;     // Memory location which caused fault
               int      si_band;     // Band event
               int      si_fd;       // File descriptor
           }

           */

void catch_signal(int signo,siginfo_t *resdata,void *unkonwp)
{
    printf("signo=%d\n",signo);
    printf("return data :%d\n",resdata->si_value.sival_int);
    printf("second return data:%d\n",resdata->si_int);
    return ;
}

int main(int arg, char *args[])
{
    pid_t pid=0;
    pid=fork();
    if(pid==-1)
    {
        printf("fork() failed! error message:%s\n",strerror(errno));
        return -1;
    }
    if(pid==0)
    {
        printf("i am child!\n");
        //等待父进程执行完信号安装
        sleep(5);
        //向父进程发送带数据的信号
        union sigval sigvalue;
        sigvalue.sival_int=222;
        //发送信号
        if(sigqueue(getppid(),SIGINT,sigvalue)==-1)
        {
            printf("sigqueue() failed ! error message:%s\n",strerror(errno));
            exit(0);
        }
        printf("子进程信号发送成功!\n");
        exit(0);
    }
    if(pid>0)
    {
        printf("i am father!\n");
        //安装信号
        struct sigaction act;
        //初始化sa_mask
        sigemptyset(&act.sa_mask);
        act.sa_sigaction=catch_signal;
        //一旦使用了sa_sigaction属性,那么必须设置sa_flags属性的值为SA_SIGINFO
        act.sa_flags=SA_SIGINFO;
        //安装信号
        if(sigaction(SIGINT,&act,NULL)!=0)
        {
            printf("sigaction() failed! \n");
            return -1;
        }
        //等待子进程返回
        int status=0;
        wait(&status);
        printf("game over!\n");
    }
    return 0;
}

Linux 改进捕捉信号机制(sigaction,sigqueue)