APUE编程:080——信号处理(sigaction处理函数)

一、函数原型

#include <signal.h>

int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);

参数:

  • signum要捕获的信号
  • act接收到信号之后对信号进行处理的结构体
  • oldact接收到信号之后,保存原来对此信号处理的各种方式与信号(可用来做备份)。如果不需要备份,此处可以填NULL

返回值:

  • 成功时:返回0
  • 出错时:返回-1,并将errno设置为指示错误

 与signal的不同,有哪些新功能?

  • signal只能捕获信号,对信号进行处理。但是不能获取信号的其它信息
  • sigaction可以使用sigaction结构体的sa_handler函数对信号进行处理(此处等同于signal函数),也可以使用sa_sigaction函数查看信号的各种详细信息
  • 并且sigaction函数还可以通过sa_mask、sa_flags对信号处理时进行很多其他操作

二、struct sigaction结构体 

struct sigaction 
{
    void     (*sa_handler)(int signum);
    void     (*sa_sigaction)(int signum, siginfo_t *info, void *p);
    
    sigset_t sa_mask;
    int      sa_flags;
    
    void     (*sa_restorer)(void);
};

 sa_handler成员:

  • 对捕获的信号进行处理的函数,函数参数为sigaction函数的参数1信号(概念上等同于单独使用signal函数)

  • 也可以设置为后面两个常量:

    • 常数SIG_IGN(向内核表示忽略此信号)

    • 常数SIG_DFL(表示接到此信号后的动作是系统默认动作)

sa_sigaction成员: 

  • sa_flags成员是SA_SIGINFO标志时,就调用此函数,可以来获取该信号的很多详细信息(而不是用来对信号进行处理)

  • signum:信号编号

  • siginfo:结构体中包含信号产生的各种详细信息

    • si_value(重点):传递的信息。通过sigqueue函数(union sigval),获取整型值si_value.sival_int或者si_value.sival_ptr传递一块内存,两者取一。
    • typedef union sigval 
      {
          int   sival_int;
          void *sival_ptr;
      }sigval_t;
    • # siginfo->si_value.sival_int = siginfo->si_int  //相等
      
      # siginfo->si_value.sival_ptr = siginfo->si_ptr  //相等
  • void *p

sa_handler和sa_sigaction函数一次只能使用其中1个:

  • sa_sigaction函数,sa_flags必须为SIGINFO

    • #  伪代码示意
      
      void my_handler(int signum, siginfo *siginfo, void *p);
      
      **********************************************************************
      
      struct sigaction act, oact;
      
      act.sa_handler = my_handler;
      sigemptyset(&act.sa_mask);
      act.sa_flags = SIGINFO;            //使用3参函数的关键
      sigaction(SIGUSER2, &act, &oact);
      
      

 sigset_t  sa_mask成员:

  • 功能:sa_mask是一个信号集,当接收到某个信号,并且调用sa_handler函数对信号处理之前,把该信号集里面的信号加入到进程的信号屏蔽字当中,当sa_handler函数执行完之后,这个信号集中的信号又会从进程的信号屏蔽字中移除
  • 为什么这样设计??这样保证了当正在处理一个信号时,如果此种信号再次发生,信号就会阻塞。如果阻塞期间产生了多个同种类型的信号,那么当sa_handler处理完之后。进程又只接受一个这种信号
  • 即使没有信号需要屏蔽,也要初始化这个成员(sigemptyset()),不能保证sa_mask=0会做同样的事情
  •  sigset_t结构体

sa_flags成员:指定信号如何处理 

SA_INTERRUPT由此信号中断的系统调用不自动重启动
SA_NOCLDSTOP

若signo是SIGCHLD:

当一子进程停止(暂停时)时(作业控制), 不产生此信号

当一子进程终止时,仍旧产生此信号

若已设置此标志,则当停止的进程继续运行时,作为XSI扩展,不产生SIGCHLD信号

(参照的下面的SA_NOCLDWAIT选项)

SA_NOCLDWAIT

若signo是SIGCHLD,则当调用进程的子进程终止时, 不创建僵死进程

若调用进程在后面调用wait,则阻塞到它所有子进程都终止,此时返回-1,errno设置为ECHILD

SA_NODEFER

当捕捉到此信号时,在执行其信号捕捉函数时,系统不自动阻塞此信号(除非sa_mask成员包括了此信号)

注意,此种类型的操作对应于早期的不可靠信号

SA_NOMASK同SA_NODEFER
SA_ONSTACK若用sigaltstack已声明了一替换栈,则此信号递送给替换栈上的进程
SA_STACK同SA_ONSTACK
SA_RESETHAND

在此信号捕捉函数的入口处,将此信号的处理方式重置为SIG_DFL,并清除SA_SIGINFO标志

注意,此种类型的信号对应于早期的不可靠信号。但是不能重置SIGILL和SIGTRAP信号的配置

设置此标志使sigaction的行为如同设置了SA_NODEFER标志

SA_ONESHOT同SA_RESETHAND
SA_RESTART由此信号中断的系统调用自动重启动
SA_SIGINFO此选项对信号处理程序提供了附加信息:一个指向siginfo结构的指针以及一个指向进程上下文标识符的
  • 说明:

    • 关于SA_INTERRUPT、SA_RESTART说明

      • 某些早期系统(如SunOS)定义了SA_INTERRUPT标志,这些系统的默认方式是重新启动被中断的系统调用,而制定此标志则使系统调用被中断后不再重新启动。Linux定义SA_INTERRUPT标志,以便与使用该标志的应用程序兼容。但是,如若信号处理程序是用sigaction设置的,那么其默认方式是不重新启动系统调用

      • Single UNIX Specifiaction的XSI扩展规定,除非说明了SA_RESTART标志,否则sigaction函数不再重启动被中断的系统调用

三、代码实现 

信号接收模块:

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

//void (*sa_sigaction)(int, siginfo_t *, void *);
//int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);

void cym_handler(int signum, siginfo_t *info, void *p)
{

    printf("n = %d\n", signum);
    printf("info->si_val.sival_int = %d\n", info->si_value.sival_int);
    printf("info->si_int = %d\n", info->si_int);
}

int main()
{
    struct sigaction newsa;
    struct sigaction oldsa;
    sigset_t newset;
    sigset_t oldset;

    newsa.sa_sigaction = cym_handler;
    sigemptyset(&newsa.sa_mask);
    newsa.sa_flags = SA_SIGINFO;

    sigaddset(&newsa.sa_mask, SIGUSR2);
    sigprocmask(SIG_BLOCK, &newsa.sa_mask, &oldset);     //阻塞方式

    if (sigaction(SIGUSR2, &newsa, &oldsa) < 0)
    {
        perror("sigaction()");
        exit(1);
    }

    while (1)
    {
        sigsuspend(&oset); //sigsuspend()不是下面几句的简单封装

        #if 0 //类似于sigsuspend(),但是不够原子,无法实现功能。
        sigset_t tmpset;
        sigprocmask(SIG_SETMASK, &oset, &tmpset); //sigprocmask和pause之间就把信号响应了,再用信号去打断pause就不可能了
        pause();//等待信号的到来
        sigprocmask(SIG_SETMASK, &tmpset, NULL);
        #endif
    }

    exit(0);
}
  • 运行接收模块,通过ps axf查看进程的pid 

信号发送模块:

#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <sys/types.h>

//int sigqueue(pid_t pid, int sig, const union sigval value);

char *buf = "Hello world";

int main(int argc, char *argv[])
{
    pid_t pid;
    union sigval sendval;

    if(argc < 2)
    {
        fprintf(stderr, "Usage.....\n");
        exit(1);
    }

    pid = atoi(argv[1]);
    sendval.sival_int = 100;

    if(sigqueue(pid, SIGUSR2, sendval) < 0)
    {
        perror("sigqueue()");
        exit(1);
    }

    exit(0);
}

运行结果:

 

参考:https://dongshao.blog.csdn.net/article/details/89278415

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值