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

函数介绍

  • 包含头文件<signal.h>
  • 功能:sigaction函数用于改变进程接收到特定信号后的行为。
  • 原型:
    int sigaction(int signum,const struct sigaction *act, 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
#endif

void 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;
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值