UNIX环境编程学习笔记(25)——信号处理进阶学习之 sigaction 函数

lienhua34
2014-11-05

sigaction 函数跟 signal 函数一样,用于设置信号处理函数。此函数是用于取代 UNIX 早期版本使用的 signal 函数。UNIX 早期版本的 signal 函数在捕获到一个信号之后,就会自动将该信号的处理动作恢复为默认处理动作。于是,如果我们希望注册的信号处理函数长期生效,则需要在信号处理程序中再次调用 signal 函数注册一次。这样的操作太麻烦,而且在信号处理函数中再次调用 signal 注册信号处理函数之前可能又会产生该信号,而这个信号就会触发默认处理动作。

另外,signal 函数还有一个限制:不修改信号的处理方式就不能确定该信号的当前处理方式。这个限制在 sigaction 函数中不存在。

sigaction 函数的声明如下,

#include <signal.h>

int sigaction(int signo, const struct sigaction *restrict act, struct sigaction *restrict oact);

返回值:若成功则返回0,若出错则返回-1

其中,signo 参数即要检测或修改其处理动作的信号编号。如果 act 指针非空,则要修改该信号的处理动作。如果 oact 指针非空,则由 oact 指针返回该信号的上一个动作。
sigaction 函数的参数中使用的结构体 struct sigaction 的声明如下,

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

该结构体各个字段的说明如下:

  • sa_handler:信号处理函数的指针。
  • sa_mask:在调用该信号的处理函数期间进程要阻塞的信号集。
  • sa_flags:指定信号进行处理的各个选项,关于具体的选项,可以参考sigaction 函数的手册。
  • sa_sigaction:替代的信号处理函数,如果sa_flags 设置了SA_SIGINFO,则会调用该信号处理函数;否则调用sa_handler。

我们来看一个例子,

复制代码
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <signal.h>

void
print_mask(const char *str)
{
  sigset_t sigset;
  int errno_save;

  errno_save = errno;
  if (sigprocmask(0, NULL, &sigset) < 0) {
    printf("sigprocmask error: %s\n", strerror(errno));
    exit(-1);
  }
  printf("%s", str);
  if (sigismember(&sigset, SIGALRM)) {
    printf("SIGALRM ");
  }
  if (sigismember(&sigset, SIGINT)) {
    printf("SIGINT ");
  }
  printf("\n");
  errno = errno_save;
}

static void
sig_alrm(int signo)
{
  printf("received SIGALRM\n");
  print_mask("in sig_alrm: ");
}

int
main(void)
{
  struct sigaction sact;

  sact.sa_handler = sig_alrm;
  sact.sa_flags = 0;
  sigemptyset(&sact.sa_mask);
  sigaddset(&sact.sa_mask, SIGINT);
  if (sigaction(SIGALRM, &sact, NULL) < 0) {
    printf("sigaction error: %s\n", strerror(errno));
    exit(-1);
  }

  print_mask("in main before alarm: ");
  alarm(3);
  pause();
  print_mask("in main after alarm: ");
  exit(0);
}
复制代码

在上面的 sigactiondemo.c 程序中,我们调用 sigaction 函数设置了信号 SIGALRM 的处理函数,而在 sigaction 函数的参数 act 的sa_mask 字段中添加了信号 SIGINT,表示调用信号 SIGALRM 的处理函数期间,进程阻塞信号 SIGINT。编译该程序,生成并执行可执行文件 sigactiondemo,

lienhua34:demo$ gcc -o sigactiondemo sigactiondemo.c
lienhua34:demo$ ./sigactiondemo
in main before alarm:
received SIGALRM
in sig_alrm: SIGALRM SIGINT
in main after alarm:

从上面的运行结果,我们看到在 SIGALRM 信号产生之前,进程没有屏蔽信号 SIGINT 和 SIGALRM,而在信号 SIGALRM 的处理函数中,进程的信号屏蔽字中包含了 SIGINT 和 SIGALRM,而在调用 SIGALRM 的信号处理函数之后,进程的信号屏蔽字复原为调用信号处理屏蔽字之前。(说明:在调用 sigaction 函数注册的信号处理函数期间,系统会自动将该信号也加入信号屏蔽字中,除非设置 sigaction 函数的 act 参数的sa_flags为SA_NODEFER。)

(done)

posted on   lienhua34  阅读(893)  评论(0编辑  收藏  举报

编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 周边上新:园子的第一款马克杯温暖上架
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
· 使用C#创建一个MCP客户端

导航

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5
点击右上角即可分享
微信分享提示