信号编程之sigaction函数和sigqueue函数

来源:互联网 发布:2016nba常规赛数据 编辑:程序博客网 时间:2024/06/01 17:05

函数介绍

  • 包含头文件<signal.h>
  • 功能:sigaction函数用于改变进程接收到特定信号后的行为。
  • 原型:
    int sigaction(int signum,const struct sigaction *act,const struct sigaction *old);
  • 参数
    • 该函数的第一个参数为信号的值,可以为除SIGKILL及SIGSTOP外的任何一 个特定有效的信号(为这两个信号定义自己的处理函数,将导致信号安装错误)
    • 第二个参数是指向结构sigaction的一个实例的指针,在结构 sigaction的实例中,指定了对特定信号的处理,可以为空,进程会以缺省方式对信号处理
    • 第三个参数oldact指向的对象用来保存原来对相应信号的处理,可指定oldact为NULL。
  • 返回值:函数成功返回0,失败返回-1

关于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只能任选其一。

  • 补充siginfo_t 结构体介绍
siginfo_t {                  int      si_signo;  /* Signal number */                  int      si_errno;  /* An errno value */                  int      si_code;   /* Signal code */                  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 */                  void *   si_addr;   /* Memory location which caused fault */                  int      si_band;   /* Band event */                  int      si_fd;     /* File descriptor */              }

示例代码

  • 最基本的用法
void handler(int sig){    if (sig == SIGINT)    {        printf("recv a sig=%d\n", sig);     }}int main(){  struct sigaction act;  act.sa_handler = handler;  sigaction(SIGINT,&act,NULL);  while(1);  return 0;}
  • 利用sigaction实现my_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>void handler(int sig){    printf("Aloha recv a sig=%d\n", sig);   }/*打造一个和signal功能一样的函数*/__sighandler_t my_signal(int sig, __sighandler_t handler){    struct sigaction act;    struct sigaction oldact;   /*在使用一个sigaction结构体之前,需要先对以下三个成员进行处理*/  act.sa_handler = handler;//指定新的处理函数    sigemptyset(&act.sa_mask);//设置信号屏蔽字    act.sa_flags = 0;    if (sigaction(sig, &act, &oldact) < 0)//注册信号,指定信号处理函数的同时备份之前的信号处理信息(包括之前该信号的处理函数)        return SIG_ERR;//注册失败--返回SID_ERR    return oldact.sa_handler;//注册成功--返回之前的信号处理函数}int main(int argc, char *argv[]){             //模拟signal函数    my_signal(SIGINT, handler);    for (;;)    {        pause();    }    return 0;}

sigqueue函数

  • 功能:新的发送信号系统调用,主要是针对实时信号提出的支持信号带有参数,与函数sigaction()配合使用。

    注意:和kill函数相比int kill(pid_t pid, int siq)多了参数

  • 原型:
    int sigqueue(pid_t pid, int sig, const union sigval value);

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

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

  • sigval联合体
typedef union sigval           {                 int sival_int;                 void *sival_ptr; }sigval_t; 

注意:

(1) 如果要让信号处理函数能够接收额外的数据,需要配合sigaction结构体中的sa_flags成员, SA_SIGINFO表示能接受数据。

(2) 信号处理函数的原型要使用void myHandle_forsigaction(int signum, siginfo_t *s_t, void *p),这个函数实在内核空间被调用的,所以形参也是在内核空间,同时,接收到的额外数据就放在siginfo_t 结构体的如下成员里。si_value和si_int是为了兼容性设置的,两个内容相同。si_ptr则对应额外数据结构体里面的void*指针部分。上述两个部分,在内核发送信号的时候,只能发送其中一部分,因为额外数据是一个union类型。

sigval_t si_value;int      si_int;    /* POSIX.1b signal */void *   si_ptr;    /* POSIX.1b 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>void handler(int sig,siginfo_t *s_t,void *p)//能够接受额外数据的信号处理函数签名{  int tmp = 0;  tmp = s_t->si_int;  //si_int和si_value.sival_int是一样的--针对额外数据是int的时候。    printf("Aloha recv a sig=%d\tvar :%d\n and var is also: %d", sig,tmp,s_t->si_value.sival_int);  }int main(int argc, char *argv[]){     pid_t     pid;    int ret = 0;  int i = 0;  union sigval mysigval;//用来存放额外数据    struct sigaction act;//用来注册信号  /*使用sigaction必须要初始化的三个成员*/  act.sa_sigaction = handler;//指定回调函数  act.sa_flags = SA_SIGINFO;//尤其重要--只有等于SA_SIGINFO,信号处理函数才能接受额外数据  sigemptyset(&act.sa_mask);//清空屏蔽字  if(sigaction(SIGINT,&act,NULL) < 0)//注册信号--指定毁掉函数  {    perror("sigaction error!\n");    exit(-1);  }  pid = fork();//创建子进程  if(-1 == pid)  {    perror("fork");    exit(-1);  }  else if(0 == pid)  {    mysigval.sival_int = 125;//设置要随着信号发送的额外数据    for(i = 0;i < 10;i++)//子进程发送十次信号--SIGINT是不可靠信号--传送有点慢    {      ret = sigqueue(getppid(),SIGINT,mysigval);//开始发送信号      if(ret != 0)//发送失败      {        perror("sigqueue");        exit(-1);      }      else{//返回0表示信号发送成功        printf("send ok!\n");        sleep(1);      }    }  }  else if(pid > 0)  {    while(1);//父进程死循环  }     return 0;}

可靠信号和不可靠信号的区别及测试

  • 可靠信号在遇到阻塞的时候需要缓存,所以缓冲区有上限,也就是发送可靠信号的数量是有限的,可用ulimit -a命令查看。
  • 可靠信号会在解除阻塞以后全部抵达,但是不可靠信号只有最后一个会抵达!
#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>/*TEST_SIG_LIMIT如果定义为1表示进行压力测试如果定义位0表示进行可靠信号和不可靠信号的区别测试*/#define TEST_SIG_LIMIT 0#if (TEST_SIG_LIMIT==1)#define MAX 1024#else#define MAX 1#endifvoid handler(int sig,siginfo_t *s_t,void *p)//能够接受额外数据的信号处理函数签名{  int tmp = 0;  sigset_t bset;  if(sig == SIGINT || sig == SIGRTMIN)  {    tmp = s_t->si_int;  //si_int和si_value.sival_int是一样的--针对额外数据是int的时候。      printf("Aloha recv a sig=%d\tvar :%d\t and var is also: %d\n", sig,tmp,s_t->si_value.sival_int);  }  else if(sig == SIGUSR1)  {    printf("unblock\n");    /*阻塞实时信号和非实时信号*/    sigemptyset(&bset);    sigaddset(&bset,SIGINT);    sigaddset(&bset,SIGRTMIN);    sigprocmask(SIG_UNBLOCK,&bset,NULL);  }  else  {    printf("other signal\n");  }}void my_handler(int sig){  sigset_t bset;  if(sig == SIGINT || sig == SIGRTMIN)  {    printf("The sig num is : %d\n",sig);  }  else if(sig == SIGUSR1)  {    printf("unblock\n");    /*阻塞实时信号和非实时信号*/    sigemptyset(&bset);    sigaddset(&bset,SIGINT);    sigaddset(&bset,SIGRTMIN);    sigprocmask(SIG_UNBLOCK,&bset,NULL);  }  else  {    printf("other signal\n");  }}int main(int argc, char *argv[]){     pid_t     pid;    int ret = 0;  int i = 0;    struct sigaction act;//用来注册信号  sigset_t bset;  union sigval v;  /*使用sigaction必须要初始化的三个成员*/  //act.sa_handler = my_handler;//指定回调函数  act.sa_sigaction = handler;  act.sa_flags = SA_SIGINFO;//尤其重要--只有等于SA_SIGINFO,信号处理函数才能接受额外数据  sigemptyset(&act.sa_mask);//清空屏蔽字  if(sigaction(SIGINT,&act,NULL) < 0)//注册非实时信号  {    perror("sigaction error!\n");    exit(-1);  }  if(sigaction(SIGRTMIN,&act,NULL) < 0)//注册实时信号  {    perror("sigaction error!\n");    exit(-1);  }  if(sigaction(SIGUSR1,&act,NULL) < 0)//注册用户自定义信号SIGUSR1  {    perror("sigaction error!\n");    exit(-1);  }  /*阻塞实时信号和非实时信号*/  sigemptyset(&bset);  sigaddset(&bset,SIGINT);  sigaddset(&bset,SIGRTMIN);  sigprocmask(SIG_BLOCK,&bset,NULL);  pid = fork();//创建子进程  if(-1 == pid)  {    perror("fork");    exit(-1);  }  else if(0 == pid)  {    v.sival_int = 0;    for(i = 1;i < MAX*1024;i++)//发送三次不可靠信号    {      v.sival_int++;      ret = sigqueue(getppid(),SIGINT,v);      if(ret != 0)      {        printf("sigqueue SIGINT failed!..ret=%d\terrno=%d\tindex=%d\n",ret,errno,i);//不可靠信号不用缓存--无限制--不会出错/不会执行!        exit(-1);      }      else{        if(i % (1024*512) == 0)        printf("send SIGINT OK!--index:%d\n",i);      }    }    v.sival_int = 0;    for(i = 1;i < MAX*1024;i++)//发送三次可靠信号    {      v.sival_int++;      ret = sigqueue(getppid(),SIGRTMIN,v);      if(ret != 0)      {        printf("sigqueue SIGRTMIN failed!..ret=%d\terrno=%d\tindex=%d\n",ret,errno,i);//可靠信号需要缓存--所以有限制--ulimit -a命令查看--此时index即上限        exit(-1);      }      else{        if(i % 512 == 0)        printf("send OK!--index:%d\n",i);      }    }    ret = kill(getppid(),SIGUSR1);//发送自定义信号--解除阻塞    if(ret == -1)    {      printf("sigqueue failed!..%d\t%d\n",ret,errno);      exit(-1);    }    else if(ret == 0){      printf("send OK!\n");    }  }  else if(pid > 0)  {    while(1);//父进程死循环  }     return 0;}
0 0
原创粉丝点击