嵌入式Linux系统编程 — 6.2 signal和 sigaction信号处理函数

news2024/12/23 9:11:39

目录

1 信号如何处理

2 signal()函数

2.1 signal()函数介绍

2.2 示例程序

3 sigaction()函数

3.1 sigaction()函数介绍

3.2 示例程序


1 信号如何处理

信号通常是发送给对应的进程,当信号到达后, 该进程需要做出相应的处理措施,可以通过以下几种方式来处理信号:

  • 忽略信号:进程可以选择忽略某些信号,使其不产生任何效果。例如,使用signalsigaction函数将信号处理函数设置为SIG_IGN。事实上,大多数信号都可以使用这种方式进行处理,但有两种信号却决不能被忽略,它们是 SIGKILL 和 SIGSTOP,它们是Linux内核保留的信号,用于立即终止和暂停进程,这两个信号的设计是为了在紧急情况下强制终止或暂停进程,确保系统能够迅速响应严重错误或管理员的干预。

  • 捕获信号:进程可以定义信号处理函数(也称为信号捕获函数或信号处理程序),当信号被发送到进程时,该函数将被调用。

  • 默认操作:如果进程没有特别指定如何处理某个信号,那么信号将执行其默认操作。例如,SIGKILL和SIGSTOP信号的默认操作是终止进程,而SIGCHLD信号的默认操作是忽略。

  • 阻塞信号:进程可以暂时阻止某些信号的传递,直到进程再次允许这些信号。这可以通过sigprocmask函数实现。

Linux 系统提供了系统调用 signal()和 sigaction()两个函数用于设置信号的处理。

2 signal()函数

2.1 signal()函数介绍

signal()函数是 Linux 系统下设置信号处理方式最简单的接口,用于定义当特定信号被触发时,进程应如何响应,可将信号的处理方式设置为捕获信号、 忽略信号以及系统默认操作。函数原型如下:

#include <signal.h>

typedef void (*sig_t)(int);
sig_t signal(int signum, sig_t handler);
  • signum:指定要设置处理方式的信号的编号。
  • handler:sig_t 类型的函数指针,指向信号对应的信号处理函数,当进程接收到信号后会自动执行该处理函数;参数 handler 既可以设置为用户自定义的函数,也就是捕获信号时需要执行的处理函数,也可以设置为 SIG_IGN 或 SIG_DFL, SIG_IGN 表示此进程需要忽略该信号, SIG_DFL 则表示设置为系统默认操作。
  • 返回值:如果成功,signal()返回指向之前信号处理函数的指针;如果失败,返回SIG_ERR

signal()函数有几个限制,包括:

  • 它不提供对信号的阻塞和非阻塞行为的控制。
  • 它不支持实时信号。
  • 它在多线程环境中可能不安全,因为它可能会改变所有线程的信号处理设置。

由于signal()函数的这些限制,现代的Linux编程推荐使用sigaction()函数,它提供了更多的控制选项,包括信号的阻塞行为、信号处理的安全性以及对实时信号的支持。

2.2 示例程序

下来编写一个简单地示例代码对signal()函数进行测试。

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>    // 包含信号处理头文件
#include <unistd.h>    // 包含UNIX标准函数定义,如sleep

void sig_handler(int sig) {
    printf("Signal %d caught\n", sig);
    exit(0);
}

int main() 
{
    signal(SIGINT, sig_handler);  // 设置SIGINT的信号处理函数
    while(1) {
        printf("Running...\n");
        sleep(1);  // 暂停程序1秒
    }
    return 0;
}

示例中,当通过Ctrl+C发送SIGINT信号时,程序将调用sig_handler函数打印信号编号,然后退出。运行结果如下: 

问题思考:如果程序中没有调用 signal()函数为信号设置相应的处理方式,亦或者程序刚启动起来并未运行到 signal()处,那么这时进程接收到一个信号后是如何处理的呢?

当一个应用程序刚启动的时候(或者程序中没有调用 signal()函数) , 通常情况下, 进程对所有信号的处理方式都设置为系统默认操作。所以如果在我们的程序当中,没有调用 signal()为信号设处理方式,则默认的处理方式便是系统默认操作。所以为什么大家平时都可以使用 CTRL + C 中断符来终止一个进程,因为大部分情况下,应用程序中并不会为 SIGINT 信号设置处理方式,所以该信号的处理方式便是系统默认操作,当接收到信号之后便执行系统默认操作,而 SIGINT 信号的系统默认操作便是终止进程。

3 sigaction()函数

3.1 sigaction()函数介绍

除了signal()之外, sigaction()系统调用是设置信号处理方式的另一选择。虽然 signal()函数简单好用,而 sigaction()更为复杂,也更具灵活性以及移植性。

sigaction()允许单独获取信号的处理函数而不是设置,并且还可以设置各种属性对调用信号处理函数时的行为施以更加精准的控制,其函数原型如下所示:

#include <signal.h>

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

  • signum:指定要操作的信号的编号。
  • act:指向一个sigaction结构的指针,该结构定义了信号的新的处理方式。如果参数 act 不为 NULL,则表示需要为信号设置新的处理方式;如果参数 act 为 NULL,则表示无需改变信号当前的处理方式。
  • oldact:(可选)指向一个sigaction结构的指针,如果参数oldact 不为 NULL, 则会将信号之前的处理方式等信息通过参数 oldact 返回出来。
  • 返回值:如果成功,sigaction()返回0;如果失败,返回-1并设置errno以指示错误。

struct sigaction 结构体的内容如下:

struct sigaction {
    void (*sa_handler)(int);
    void (*sa_sigaction)(int, siginfo_t *, void *);
    sigset_t sa_mask;
    int sa_flags;
    void (*sa_restorer)(void);
};
  • void (*sa_handler)(int):信号处理函数。当信号发生时,如果sa_flags没有设置SA_SIGINFO标志,内核将调用此函数。该函数接收一个整数参数,即捕获的信号编号。

  • void (*sa_sigaction)(int, siginfo_t *, void *):指向一个更复杂的信号处理函数。如果sa_flags设置了SA_SIGINFO标志,内核将调用此函数,允许访问更详细的信号信息。它接收三个参数:信号编号、指向siginfo_t结构的指针siginfo_t 结构体用于在信号处理函数中提供有关信号的详细信息,如信号编号、错误编号等),以及一个不透明指针(通常用于传递信号处理函数的特定上下文)。

  • sigset_t sa_mask:一个信号集,定义了在信号处理函数执行期间应该被屏蔽的信号。这意味着在信号处理函数执行时,这些信号不会被传递给进程。

  • int sa_flags:参数 sa_flags 指定了一组标志,这些标志用于控制信号的处理过程,可设置为如下这些标志(多个标志使用位或" | "组合):

标志功能
SA_NOCLDSTOP如果signum为SIGCHLD, 则子进程停止时(即当它们接收到SIGSTOP、SIGTSTP、SIGTTIN 或SIGTTOU中的一种时)或恢复(即它们接收到 SIGCONT)时不会收到 SIGCHLD 信号。
SA_NOCLDWAIT如果 signum 是 SIGCHLD,则在子进程终止时不要将其转变为僵尸进程。
SA_NODEFER不要阻塞从某个信号自身的信号处理函数中接收此信号。 也就是说当进程此时正在执行某个信号的处理函数,默认情况下,进程会自动将该信号添加到进程的信号掩码字段中,从而在执行信号处理函数期间阻塞该信号, 默认情况下,我们期望进程在处理一个信号时阻塞同种信号,否则引起一些竞态条件;如果设置了 SA_NODEFER 标志,则表示不对它进行阻塞。
SA_RESETHAND执行完信号处理函数之后,将信号的处理方式设置为系统默认操作。
SA_RESTART被信号中断的系统调用,在信号处理完成之后将自动重新发起。
SA_SIGINFO如果设置了该标志,则表示使用 sa_sigaction 作为信号处理函数、而不是 sa_handler,关于 sa_sigaction信号处理函数的参数信息。
  • void (*sa_restorer)(void):指向一个在信号处理完成后调用的函数。这个成员在现代的POSIX兼容系统中已经不推荐使用,并且在许多现代系统中已经被忽略或移除。在早期的UNIX系统中,它用于指定一个函数,该函数负责在信号处理返回后恢复信号处理函数可能改变的寄存器状态。

3.2 示例程序

下面是一个使用sigaction()函数的示例程序:

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

// 信号处理函数
void sig_handler(int signum, siginfo_t *info, void *ptr) {
    printf("Signal %d caught\n", signum);
    if (info != NULL) {
        printf("Info: si_code=%d, si_errno=%d\n", info->si_code, info->si_errno);
    }
    // 执行清理工作,然后退出
    exit(0);
}

int main() 
{
    struct sigaction sa;

    // 初始化sigaction结构
    sa.sa_sigaction = sig_handler; // 设置信号处理函数
    sa.sa_flags = SA_SIGINFO;     // 指定使用sa_sigaction

    // 初始化信号集,这里我们不屏蔽任何信号
    if (sigemptyset(&sa.sa_mask) < 0) {
        perror("sigemptyset");
        exit(EXIT_FAILURE);
    }

    // 设置SIGINT信号的处理动作
    if (sigaction(SIGINT, &sa, NULL) < 0) {
        perror("sigaction");
        exit(EXIT_FAILURE);
    }

    printf("Waiting for SIGINT...\n");
    // 主循环,等待信号发生
    while(1) {
        sleep(1);
    }

    return 0;
}

程序定义了一个信号处理函数sig_handler,它接收三个参数:信号编号、指向siginfo_t结构的指针和一个不透明指针。在main函数中,初始化sigaction结构,并设置sa_sigactionsig_handler,将sa_flags设置为SA_SIGINFO,表示们希望使用siginfo_t结构接收信号信息。然后,调用sigaction()函数来注册SIGINT信号的处理动作。程序进入一个无限循环,等待接收SIGINT信号(通常由用户通过Ctrl+C触发)。当SIGINT信号发生时,sig_handler函数将被调用,并打印出信号信息。 运行结果如下:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1885671.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ROS——Server、Client的编程实现,服务数据的定义与使用,参数的使用和编程方法

客户端 创建功能包 创建客户端、创建服务、配置请求数据、请求调用、等待应答 设置编译规则 编译运行客户端 服务端 创建服务器 编译成目标执行文件、相关库的连接 add_executable(turtle_command_server src/turtle_command_server.cpp) target_link_libraries(turtle_comman…

第2章 寄存器

第2章 寄存器 一个 CPU 由运算器&#xff0c;控制器&#xff0c;寄存器等器件构成&#xff0c;这些器件靠内部总线进行相连。简单的说 CPU 中&#xff1a; 运算器进行信息处理寄存器进行信息存储控制器控制各种器件进行工作内部总线连接各个器件&#xff0c;在他们之间进行各种…

工业交换机端口统计功能

工业交换机端口统计功能不仅是一项技术手段&#xff0c;更是一双透视企业网络健康状态的慧眼。通过这一功能&#xff0c;企业能够实时捕捉到网络中每一个端口的流量情况&#xff0c;这不仅仅是数据的积累&#xff0c;更是对网络脉搏的精准把握。当网络的每一个脉动都被记录在案…

git基本使用(一):git的基本概念

Git 是一种分布式版本控制系统&#xff0c;最初由 Linus Torvalds 于 2005 年为 Linux 内核开发。它主要用于跟踪文件的更改&#xff0c;特别是在软件开发过程中&#xff0c;可以帮助团队成员协同工作。它在实际项目开发中&#xff0c;应用非常广泛&#xff0c;我们这一节来掌握…

第2章.现场设备的物联网模式--设备管理

2.3 设备管理 设备的作用及其管理方式是物联网和非物联网部署之间的关键区别。设备生命周期管理的阶段包括设备供应&#xff08;注册、激活和调试&#xff09;到取消供应。本书中用于设备管理的注释如下图所示&#xff1a; 图2.6——设备管理模式的符号 设备管理包括现场设备的…

Power Platform功能管理实战课程重点学什么?怎么学?

Power Platform 由 Power BI、Power Apps、Power Automate、Power Virtual Agents 四大模块组成。 这几个模块单独应用都具有强大的功能&#xff0c;而微软把它们放在一起&#xff0c;相互协作、相得益彰&#xff0c;形成了强大的生态系统。而这也是Power Platform的核心理念—…

Java案例实现双色球

一问题&#xff1a; 二具体代码&#xff1a; package 重修;import java.util.Random; import java.util.Scanner;public class first {public static void main(String[] args) {int []usersnumbersusernumslect();System.out.println("用户");for (int i 0; i <…

springboot个人证书管理系统-计算机毕业设计源码16679

摘要 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;管理信息系统的实施在技术上已逐步成熟。本文介绍了个人证书管理系统的开发全过程。通过分析个人证书管理系统管理的不足&#xff0c;创建了一个计算机管理个人证书管理系统的方案。文章介绍了个人证书管理系统的系…

计算机系统基础(二)

1.数值数据的表示 为什么采用二进制&#xff1f; 二进制只有两种基本状态&#xff0c;两个物理器件就可以表示0和1二进制的编码、技术、运算规则都很简单0和1与逻辑命题的真假对应&#xff0c;方便通过逻辑门电路实现算术运算 数值数据表示的三要素 进位记数制&#xff08;十…

开发数字药店APP实战:互联网医院系统源码详解

本篇文章&#xff0c;笔者将深入探讨如何开发一个功能完善的数字药店APP&#xff0c;并详细解析互联网医院系统的源码实现。 一、数字药店APP的需求分析 应具备以下基本功能&#xff1a; 用户注册与登录 药品搜索与浏览 在线下单与支付 订单管理 健康咨询与远程医疗 个人…

发电机保护屏组成都有哪些,如何选择

发电机保护屏组成都有哪些&#xff0c;如何选择 发电机是电力系统中最常用的一种电力设备。例如水力发电机&#xff0c;柴油发电机&#xff0c;风力发电机&#xff0c;火力发电等等。发电机保护是保证发电机安全、稳定运行的重要手段之一。对于一些小型机组的发电机&#xff0c…

Mysql部署MHA高可用

部署前准备&#xff1a; mysql-8.0.27下载地址&#xff1a;https://cdn.mysql.com//Downloads/MySQL-8.0/mysql-8.0.27-1.el7.x86_64.rpm-bundle.tar mha-manager下载地址&#xff1a;https://github.com/yoshinorim/mha4mysql-manager/releases/download/v0.58/mha4mysql-mana…

因为自己淋过雨所以想给嵌入式撑把伞

在开始前刚好我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「嵌入式的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿共享给大家&#xff01;&#xff01;&#xff01;新手学嵌入式&#xff0c;…

PHP验证日本固定电话号码

日本电话号码格式众多&#xff0c;验证起来比较头大&#xff0c;现在咱们来一个简单的总结哈 为了简单起见&#xff0c;使用PCRE 函数preg_match通过匹配正则表达式来实现验证。 function checkGdTelLandline(string $str): int|false {return preg_match("/\A0(\d{1}[-…

积鼎CFD:基于Virtualflow在溃坝洪水演进数值仿真分析

近期&#xff0c;多地遭受了前所未有的洪水侵袭&#xff0c;每一次灾害都如同警钟长鸣&#xff0c;提醒我们水库大坝安全的重要性&#xff0c;超标准暴雨的突袭让大坝的安全防线面临前所未有的考验。面对这一挑战&#xff0c;CFD技术可为溃坝洪水的预测与管理开辟新的路径。凭借…

python+django 环境搭建以及post接口封装

1、搭建pythondjango环境 python 3.7.9的版本 具体参考之前的安装教程 django 使用 pip install django 会自动安装 检验安装版本&#xff1a; python -m django --version 2、创建django项目 django-admin startproject projectname 启动项目&#xff1a;python manage.py…

基于X86+FPGA+AI的切割机控制、六轴机器人控制方案

一、智能设备控制 应用场景 智能制造(Intelligent Manufacturing, IM)是一种由智能机器和人类专家共同组成的人机一体化智能系统&#xff0c;它在制造过程中能进行智能活动&#xff0c;诸如分析、推理、判断、构思和决策等。通过人与智能机器的合作共事&#xff0c;去扩大、延…

力扣 用队列实现栈(Java)

核心思想&#xff1a;因为队列都是一端进入另一端出&#xff08;先进先出&#xff0c;后进后出&#xff09;&#xff0c;因此一个队列肯定是不能实现栈的功能的&#xff0c;这里就创建两个队列来模拟栈的先进后出&#xff0c;后进先出。 比如说如果是push操作我们肯定是要弹出栈…

用四个场景案例,分析使用大模型对程序员工作的帮助提升_大模型应用场景

引言 随着人工智能技术的不断发展&#xff0c;大模型在软件开发中的应用越来越广泛。 这些大模型&#xff0c;如GPT、文心一言、讯飞星火、盘古大模型等&#xff0c;可以帮助程序员提高工作效率&#xff0c;加快开发速度&#xff0c;并提供更好的用户体验。 本文将介绍我在实…

MeowBot:ESP32 语音控制宠物猫 DIY 教程——玩转语音识别与 MQTT 智能家居控制 (附代码解析)

摘要: 本文将手把手教你打造一只名为 MeowBot 的智能宠物猫&#xff01;它不仅可以通过舵机灵动地打招呼&#xff0c;还能听懂你的语音指令&#xff0c;帮你控制智能家居设备。让我们一起开启这段充满乐趣的 DIY 之旅吧&#xff01; 关键词: ESP32、语音识别、MQTT、智能家居、…