linux信号(4):信号安装函数sigaction和信号发送函数sigqueue

Sigaction 信号安装  

signaction 和signal都是信号安装函数

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

功能:

   sigaction函数用于改变进程接收到特定信号后的行为。

参数

   第一个参数为信号的值,可以为除SIGKILL及SIGSTOP外的任何一个特定有效的信号(为这两个信号定义自己的处理函数,将导致信号安装错误)

   第二个参数是指向结构sigaction的指针,在结构 sigaction的实例中,指定了对特定信号的处理,可以为空,进程会以缺省方式对信号处理

   第三个参数oldact指向的对象用来保存原来对相应信号的处理,可指定oldact为NULL。

返回值:函数成功返回0,失败返回-1

sigaction结构体

  第二个参数最为重要,其中包含了对指定信号的处理、信号所传递的信息、信号处理函数执行过程中应屏蔽掉哪些函数等等

struct sigaction {
//信号处理程序 不接受额外数据(比较过时)
	void (*sa_handler)(int);
 
//信号处理程序能接受额外数据,和sigqueue配合使用(支持信号排队,信号传送其他信息),推荐使用
	void (*sa_sigaction)(int, siginfo_t *, void *); 		
 
       sigset_t sa_mask; 	//屏蔽
	int sa_flags; 		//表示信号的行为:SA_SIGINFO表示能接受数据
	void (*sa_restorer)(void); //废弃不用了
};
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <fcntl.h>

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


#define ERR_EXIT(m) \
    do \
    { \
        perror(m); \
        exit(EXIT_FAILURE); \
    } while(0)

void handler(int sig);

int main(int argc, char *argv[])
{
    struct sigaction act;
    act.sa_handler = handler;
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;
    //因为不关心SIGINT上一次的struct sigaction所以,oact为NULL
    //与signal(handler,SIGINT)相同
    if (sigaction(SIGINT, &act, NULL) < 0)
        ERR_EXIT("sigaction error\n");

    for (;;)
        pause();
    return 0;
}

void handler(int sig)
{
    printf("recv a sig=%d\n", sig);
}

sa_mask指定在信号处理程序执行过程中,哪些信号应当被阻塞。缺省情况下当前信号本身被阻塞,防止信号的嵌套发送,除非指定SA_NODEFER或者SA_NOMASK标志位,处理程序执行完后,被阻塞的信号开始执行。

注:请注意sa_mask指定的信号阻塞的前提条件,是在由sigaction()安装信号的处理函数执行过程中由sa_mask指定的信号才被阻塞。

//利用sigaction实现signal

#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <fcntl.h>

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


#define ERR_EXIT(m) \
    do \
    { \
        perror(m); \
        exit(EXIT_FAILURE); \
    } while(0)

void handler(int sig);
__sighandler_t my_signal(int sig, __sighandler_t handler);

int main(int argc, char *argv[])
{
    my_signal(SIGINT, handler);
    for (;;)
        pause();
    return 0;
}

__sighandler_t my_signal(int sig, __sighandler_t handler)
{
    struct sigaction act;
    struct sigaction oldact;
    act.sa_handler = handler;
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;

    if (sigaction(sig, &act, &oldact) < 0)
        return SIG_ERR;

    return oldact.sa_handler;
}

void handler(int sig)
{
    printf("recv a sig=%d\n", sig);
}

验证sigaction.sa_mask效果

#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <fcntl.h>

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


#define ERR_EXIT(m) \
    do \
    { \
        perror(m); \
        exit(EXIT_FAILURE); \
    } while(0)

void handler(int sig);

int main(int argc, char *argv[])
{
    struct sigaction act;
    act.sa_handler = handler;
    sigemptyset(&act.sa_mask);
    sigaddset(&act.sa_mask, SIGQUIT);  //屏蔽SIGQUIT
    act.sa_flags = 0;

    if (sigaction(SIGINT, &act, NULL) < 0)
        ERR_EXIT("sigaction error");

    struct sigaction act2;
    act2.sa_handler = handler;
    sigemptyset(&act2.sa_mask);
    act2.sa_flags = 0;

    if (sigaction(SIGQUIT, &act2, NULL) < 0)
        ERR_EXIT("sigaction error");

    for (;;)
        pause();
    return 0;
}

void handler(int sig)
{
    if(sig == SIGINT){

        printf("recv a SIGINT signal\n");
        sleep(5);
    }
    if (sig == SIGQUIT)
    {
        printf("recv a SIGQUIT signal\n");
    }
}

可知,安装信号SIGINT时,将SIGQUIT加入到sa_mask阻塞集中,则当SIGINT信号正在执行处理函数时,SIGQUIT信号将被阻塞,只有当SIGINT信号处理函数执行完后才解除对SIGQUIT信号的阻塞,由于SIGQUIT是不可靠信号,不支持排队,所以只递达一次。

signal函数重定向信号的处理函数,默认为阻塞(BLOCK)模式,就是说在运行一个信号的处理函数时,再收到同样的信号,将直接被阻塞,而不是再次立即调用处理函数(不允许中断嵌套);直到这一次的信号处理函数调用完成之后,未决的被阻赛的中断会继续被发送给进程,从而进程可以再次调用信号处理函数。

sigaction.sa_mask可以设定调用信号处理函数时需要阻塞的信号;sigaction.sa_flags默认为0,效果跟signal类似,阻塞本身信号;如果设置为SA_NODEFER,那么在运行信号的处理函数时再次收到当前的信号,也会触发新处理函数调用,这些处理函数的通过递归的方式调用(允许中断嵌套)。

sigaction.sa_mask表示在执行handler 的时候, 如果此时进程收到了sa_mask所包含的信号, 则这些信号将不会被响应, 直到handler函数执行完毕。sigprocmask不同是: sigprocmask 是指定该进程的信号屏蔽字,屏蔽相应的信号,还没有进入处理函数就已经屏蔽

sigqueue

#include <signal.h>
int sigqueue(pid_t pid, int sig, const union sigval value);

sigqueue的第一个参数是指定接收信号的进程id,第二个参数确定即将发送的信号,第三个参数是一个联合数据结构union sigval,指定了信号传递的参数,即通常所说的4字节值。返回值成功返回0,失败返回-1

sigqueue()kill()传递了更多的附加信息,但sigqueue()只能向一个进程发送信号,而不能发送信号给一个进程组。

typedef union sigval{
    int sival_int;
    void *sival_ptr;
} sigval_t;
//发送端
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>

int main(int argc, char **argv)
{
    if(argc != 2){
        fprintf(stderr,"usage:%s pid\n",argv[0]);
        exit(EXIT_FAILURE);
    }
    pid_t pid = atoi(argv[1]);    
    sleep(2);
    union sigval mysigval;
    mysigval.sival_int = 100;
    printf("sending SIGINT signal to %d......\n",pid);
    if(sigqueue(pid,SIGINT,mysigval) == -1){
        perror("sigqueue error");
        exit(EXIT_FAILURE);
    }
    return 0;
}
//接收端
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>

void sighandler(int signo, siginfo_t *info,void *ctx);
//给自身传递信息
int main(void)
{

    struct sigaction act;
    act.sa_sigaction = sighandler;
    sigemptyset(&act.sa_mask);
    act.sa_flags = SA_SIGINFO;//信息传递开关
    if(sigaction(SIGINT,&act,NULL) == -1){
        perror("sigaction error");
        exit(EXIT_FAILURE);
    }
    for(; ;){
        printf("waiting a SIGINT signal....\n");
        pause();
    }
    return 0;
}

void sighandler(int signo, siginfo_t *info,void *ctx)
{
    //以下两种方式都能获得sigqueue发来的数据
    printf("receive the data from siqueue by info->si_int is %d\n",info->si_int);
    printf("receive the data from siqueue by info->si_value.sival_int is %d\n",info->si_value.sival_int);

}

发送端发送一个SIGINT信号,接收端接收到信号,取出接收到的信号信息siginfo

也可以给自身发送信号

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

void sighandler(int signo, siginfo_t *info,void *ctx);
//给自身传递信息
int main(void)
{

    struct sigaction act;
    act.sa_sigaction = sighandler;
    sigemptyset(&act.sa_mask);
    act.sa_flags = SA_SIGINFO;//信息传递开关
    if(sigaction(SIGINT,&act,NULL) == -1){
        perror("sigaction error");
        exit(EXIT_FAILURE);
    }
    sleep(2);
    union sigval mysigval;
    mysigval.sival_int = 100;
    if(sigqueue(getpid(),SIGINT,mysigval) == -1){
        perror("sigqueue error");
        exit(EXIT_FAILURE);
    }
    return 0;
}

void sighandler(int signo, siginfo_t *info,void *ctx)
{
    //以下两种方式都能获得sigqueue发来的数据
    printf("receive the data from siqueue by info->si_int is %d\n",info->si_int);
    printf("receive the data from siqueue by info->si_value.sival_int is %d\n",info->si_value.sival_int);

}

注意发送的是可靠信号的话支持排队,不可靠信号不支持排队,会丢失

猜你喜欢

转载自blog.csdn.net/Alatebloomer/article/details/81806783