当前位置: 首页 > news >正文

深入理解sigaction函数:Linux信号处理机制与使用指南

目录

一、函数原型

二、功能概述

三、参数详解

1、signo

2、act

1. sa_handler

2. sa_mask

3. sa_flags

4. sa_sigaction

3、oact

四、使用示例

1、初始化阶段

2、注册信号处理

3、等待信号

4、信号递达过程(当按下 Ctrl+C)

5、三个表的状态变化

6、程序行为

7、关键特点

五、信号处理流程

六、注意事项

1、信号处理函数的简洁性

2、信号屏蔽字的设置

3、错误处理


一、函数原型

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

二、功能概述

  • sigaction 函数是 Unix/Linux 系统中用于读取和修改与指定信号相关联的处理动作的核心函数。

  • 通过调用该函数,程序可以灵活地控制信号的处理方式,包括忽略信号、执行系统默认动作或自定义信号处理函数。调用成功时返回 0,失败则返回 -1 并设置相应的错误码。

  • 这个函数同之前学过的sigprocmask 函数有异曲同工之妙!!!可以类比来学习!!!


三、参数详解

1、signo

  • 指定要操作或查询的信号编号,例如 SIGINT(中断信号,通常由 Ctrl+C 触发)、SIGQUIT(退出信号,通常由 Ctrl+\ 触发)等。

  • 信号编号是系统预定义的常量,不同系统可能支持的信号种类和编号略有差异,但常见的信号在大多数系统中都是一致的。我们可以使用kill -l命令查看我们目前系统中的信号:

2、act

  • 指向 struct sigaction 结构体的指针,用于指定新的信号处理动作。

  • 如果 act 为 NULL,则表示不修改当前信号的处理动作,仅查询原有动作。

struct sigaction 结构体的详细定义如下:

struct sigaction {void (*sa_handler)(int);      // 信号处理函数指针或特殊常量sigset_t sa_mask;             // 信号屏蔽字int sa_flags;                 // 标志位,控制信号处理行为void (*sa_sigaction)(int, siginfo_t *, void *); // 实时信号处理函数(本章不详细讨论)
};

1. sa_handler

可以赋值为以下三种值之一:

  • SIG_IGN:表示忽略该信号。当信号递达时,系统将直接丢弃该信号,不会执行任何处理动作。

  • SIG_DFL:表示执行系统默认动作。不同信号的默认动作可能不同,例如 SIGINT 的默认动作是终止进程,SIGCHLD 的默认动作是忽略该信号。

  • 函数指针:指向一个用户自定义的信号处理函数。该函数返回类型为 void,可以带一个 int 参数,通过该参数可以得知当前信号的编号,从而可以用同一个函数处理多种信号。需要注意的是,信号处理函数是一个回调函数,它不是由 main 函数直接调用,而是由系统在信号递达时自动调用。

2. sa_mask

  • 用于指定在调用信号处理函数期间需要额外屏蔽的信号集合。

  • 当某个信号的处理函数被调用时,内核会自动将当前信号加入进程的信号屏蔽字,即阻塞当前信号,防止在信号处理函数执行期间再次递达该信号,从而导致递归调用。(这个是它的作用和使用时机,很重要!!!)

  • 如果希望在调用信号处理函数时自动屏蔽其他一些信号,可以在 sa_mask 中设置这些信号的掩码。当信号处理函数返回时,内核会自动恢复原来的信号屏蔽字。(还有恢复处理执行动作!!!同样重要!!!)

3. sa_flags

        包含一些标志位,用于控制信号处理的行为。在本章的代码示例中,通常将 sa_flags 设为 0,表示采用默认行为。常见的标志位包括:

  • SA_RESTART:如果信号中断了某个系统调用,设置该标志位后,系统调用会自动重新启动,而不是返回错误。

  • SA_NOCLDSTOP:对于 SIGCHLD 信号,设置该标志位后,当子进程停止或继续执行时,不会产生 SIGCHLD 信号。

4. sa_sigaction

        是用于实时信号的处理函数,与 sa_handler 类似,但提供了更多的信息。在本章中不进行详细讨论,有兴趣的读者可以进一步了解实时信号处理机制。

3、oact

  • 如果 oact 是非空指针,则函数会将进程当前的act,也就是 struct sigaction 类型的结构体内容通过 oact 参数传出。这样,调用者就可以获取到进程当前的指定信号原来的处理动作。(输出型参数)

  • 如果 oact 为 NULL,则表示不关心原有处理动作,仅设置新的处理动作。


四、使用示例

下面是一个使用 sigaction 函数设置自定义信号处理函数的示例代码:

#include <stdio.h>
#include <signal.h>
#include <unistd.h>// 自定义信号处理函数
void my_handler(int signo) {printf("Received signal: %d\n", signo);
}int main() {struct sigaction act, oact;// 设置新的信号处理动作act.sa_handler = my_handler; // 指定自定义处理函数sigemptyset(&act.sa_mask);  // 清空信号屏蔽字,不额外屏蔽任何信号act.sa_flags = 0;           // 采用默认标志位// 注册 SIGINT 信号的处理函数if (sigaction(SIGINT, &act, &oact) == -1) {perror("sigaction error");return 1;}printf("Press Ctrl+C to trigger SIGINT signal...\n");// 无限循环,等待信号触发while (1) {pause(); // 暂停进程,等待信号递达}return 0;
}

1、初始化阶段

struct sigaction act, oact;
act.sa_handler = my_handler;  // 设置处理函数
sigemptyset(&act.sa_mask);    // 不额外屏蔽信号  
act.sa_flags = 0;             // 默认标志
  • 创建新的信号动作结构体 act

  • 指定信号处理函数为 my_handler

  • 设置空的信号屏蔽字(执行handler时不阻塞其他信号)

2、注册信号处理

sigaction(SIGINT, &act, &oact);

内核表变化

  • handler表:SIGINT 的处理函数改为 my_handler

  • 保存旧的处理方式到 oact(可用于恢复)

3、等待信号

while (1) {pause(); // 暂停进程,等待信号
}
  • pause() 使进程挂起,直到收到信号

  • 此时进程处于可中断的睡眠状态

4、信号递达过程(当按下 Ctrl+C)

  1. 信号产生:内核检测到 Ctrl+C,生成 SIGINT(2) 信号

  2. 检查阻塞:检查进程的 block 表,SIGINT 未被阻塞

  3. 执行处理

    • 内核临时将 SIGINT 加入进程的 block 表(防止重入,也就是防止再次执行对应信号的处理函数)

    • 调用 my_handler(2) 执行自定义处理

    • 恢复原来的 block 表

  4. 继续运行:处理完成后,进程从 pause() 返回,继续循环

5、三个表的状态变化

表类型初始状态信号产生时处理过程中
handler表SIGINT → my_handler不变不变
block表SIGINT未阻塞不变临时阻塞SIGINT
pending表全0SIGINT位=1清0再处理

6、程序行为

  • 运行:程序启动后显示提示信息,然后挂起

  • 触发:按下 Ctrl+C 时执行 my_handler 打印消息

  • 结果:每次 Ctrl+C 都会打印 "Received signal: 2",程序继续运行

7、关键特点

  • 使用 sigaction:比 signal 更可靠,可移植性更好

  • 自动防重入:执行handler时自动阻塞同种信号,直到执行完当前的信号处理函数后再清除当前的阻塞

  • 不会终止:自定义handler改变了SIGINT的默认终止行为

这是一个典型的信号捕获示例,展示了如何改变信号的默认行为!


五、信号处理流程

当使用 sigaction 注册了自定义信号处理函数后,信号的处理流程如下:

  1. 当信号递达至进程时,内核会检查该信号是否被忽略或设置了默认处理动作。如果设置了自定义处理函数,内核会保存当前进程的上下文(包括寄存器状态、堆栈指针等),然后切换到信号处理函数的堆栈空间。

  2. 调用自定义信号处理函数,并将信号编号作为参数传递给该函数。

  3. 信号处理函数执行完毕后,内核会自动恢复之前保存的进程上下文,使进程从被中断的位置继续执行。

  4. 如果在信号处理函数执行期间,有其他信号递达且该信号被包含在 sa_mask 中,这些信号会被阻塞,直到信号处理函数返回后才被处理。


六、注意事项

1、信号处理函数的简洁性

  • 由于信号处理函数是在一个相对独立的环境中执行的,且可能在任何时候被调用,因此应尽量保持信号处理函数的简洁,避免执行复杂的操作,如调用不可重入函数(如 printfmalloc 等),以免引发竞态条件或其他问题。

2、信号屏蔽字的设置

  • 合理设置 sa_mask 可以避免信号处理过程中的竞态条件。

  • 例如,在处理某个信号时,如果该信号的处理函数会修改某些共享数据,可以屏蔽其他可能访问这些数据的信号,确保数据的一致性。

3、错误处理

  • 在使用 sigaction 函数时,应始终检查其返回值,以确保操作成功。

  • 如果调用失败,可以通过 perror 等函数输出错误信息,便于调试。

通过合理使用 sigaction 函数,程序可以实现对信号的灵活控制,提高程序的健壮性和可靠性。

http://www.dtcms.com/a/533316.html

相关文章:

  • 网站设计公司哪家专业在线种子资源网
  • 便宜做网站如何免费做网站赚钱
  • 4 Initialization and Training(初始化和训练)
  • 简易银行系统->多线程高并发
  • 【系统分析师】高分论文:论基于构件的软件开发(气象灾害影响评估系统)
  • 【java面向对象进阶】------多态综合案例
  • 做一个内容网站多少钱wordpress调用导航
  • 惠州网站开发公司电话个人兴趣图片集网站建设
  • C++ list 类的使用
  • 怎么利用QQ空间给网站做排名没有专项备案的网站
  • 显示官网字样的网站怎么做wordpress 多站点主题
  • 如何建设一个新的网站快手推广软件免费版
  • 国内十大搜索引擎网站安徽网络关键词优化
  • 【Linux】定制Linux系统
  • 个人网站备案取消网站建设管理分工
  • 企业网站建设的趋势手机网站建设的整体流程图
  • 松原做招聘的网站有哪些系统优化的意义
  • 业务流程图 —— 讲清“谁做了什么事”
  • 天津网站建设咨询杭州景观设计公司排行
  • 10.5 小项目:如何用 JavaScript 实现一个高效的时间+Token过期容器?
  • 网站备案帐号找回密码免费友链平台
  • Every other Cycle Command Input(隔循环命令输入)
  • 更换docker默认镜像仓库地址方法
  • 福建省交通建设质量安全监督局网站软文写作技巧
  • 装饰公司营销型网站设计网站图片等比缩小
  • 高端网站制作价格大型大型网站建设方案ppt模板
  • 【底层机制】Linux内核4.10版本的完整存储栈架构讲解--用户空间到物理设备完整IO路径
  • nodejs 使用speaker + ffmpeg 实现静默播放MP3
  • 【大模型应用开发 LangChain模型输出属性速查表】
  • 南京 电子商务网站WordPress实现微信一键登录