Linux【C编程】 信号以及信号的处理方式

news2025/2/25 0:53:44

文章目录

  • 1.什么是信号?
    • 1.1信号是内容受限的一种异步通信机制
    • 1.2信号由谁发出的?
    • 1.3信号由谁处理,如何处理
  • 2.常见的信号
  • 3.进程对信号的处理
    • 3.1用signal函数处理SIGINT信号
    • 3.2使用sigaction 函数
  • 4.alarm 和pause函数
    • 4.1 alarm函数详解
    • 4.2 pause函数详解

1.什么是信号?

1.1信号是内容受限的一种异步通信机制

在Linux中,信号是一种进程间通信(IPC)的机制。它用于通知某个进程发生了某种事件或异常。信号的本质是一种软件层次的模拟硬件中断的方法。当某个事件发生时,操作系统会向该事件的关联进程发送一个信号。进程接收到信号后,可以选择忽略该信号或者执行相应的操作来响应该信号。
可以总结为以下三点:
(1)信号的目的:用来通信
(2)信号是异步的(对比硬件中断)
(3)信号的本质上是int型数字编号(事先定义好的)

1.2信号由谁发出的?

信号可以由用户、系统或进程发送给目标进程。用户可以通过键盘输入、终端命令或系统调用等方式发送信号。系统则会在某些特定事件发生时自动发送信号,例如进程终止、硬件异常等。进程也可以通过系统调用或信号发送函数来发送信号给其他进程。在Linux中,可以使用kill命令或raise函数来向其他进程发送信号。某种软件条件满足后也会发出信号,如alarm闹钟时间到会产生SIGALARM信号,向一个读端已经关闭的管道write是会产生SIGPIPE信号。

1.3信号由谁处理,如何处理

  • 忽略信号:进程可以选择忽略收到的信号。这是大多数程序对信号的处理方式。但是,有些信号是不能被忽略的,例如SIGKILL和SIGSTOP,因为它们提供了使进程终止或停止的可靠方法。
  • 默认处理:如果进程没有为某个信号设置自定义的处理函数,那么系统会为该信号提供默认的处理方式。大多数信号的默认动作为终止进程。
  • 捕获信号:进程可以注册一个信号处理函数来指定如何响应某个信号。当进程接收到该信号时,会调用相应的处理函数来执行相应的操作。

2.常见的信号

1.SIGINT (2): 中断信号。通常由用户按下Ctrl + C发送,用于终止正在运行的程序。
2.SIGKILL (9): 杀死信号。这是一个无法被捕获或忽略的信号,强制终止进程。
3.SIGTERM (15): 终止信号。通常用于请求进程正常终止。与SIGKILL不同,进程可以捕获并且可以执行清理工作后再退出。
4.SIGSEGV (11): 段错误信号。当进程访问非法内存时触发,通常表示有bug导致了内存访问错误。
5.SIGILL (4): 非法指令信号。当进程试图执行非法的CPU指令时触发。
6.SIGHUP (1): 挂起信号。通常在与终端的连接断开时发送给进程,要求进程重新加载配置或重新初始化。
7.SIGUSR1 (10)和 SIGUSR2 (12): 用户自定义信号1和2。可以由用户自定义用途。
8.SIGALRM (14): 闹钟信号。通常由alarm()函数设置的定时器超时时发送给进程。

3.进程对信号的处理

3.1用signal函数处理SIGINT信号

当进程接收到SIGINT信号时,可以注册一个signal函数来执行后台保存操作等。
捕获Ctrl+C 终止信号 代码演示

#include <stdio.h>  
#include <stdlib.h>  
#include <signal.h>  
#include <unistd.h>  
  
void handle_signal(int signal_number) {  
    printf("捕获到信号 %d\n", signal_number);  
    printf("我要退出了");
    exit(0);  // 退出程序  
}  
  
int main() {  
    // 注册SIGINT信号的处理函数为handle_signal  
    signal(SIGINT, handle_signal);  
    while (1) {  
        printf("程序运行中... 正在等待信号\n");  
        sleep(1);  // 暂停1秒  
    }  
  
    return 0;  
}

signal函数是用于注册信号处理程序的标准函数之一。有一些优点和缺点:
优点:

  1. 简单易用:
    signal函数是C标准库提供的,使用起来相对简单,能够快速地注册信号处理函数。
  2. 基本功能完备:
    它提供了基本的信号处理功能,允许你指定在接收到特定信号时应该执行的处理函数。

缺点:无法简单地直接得知之前设置的对信号的处理方法。如果你使用 signal(SIGINT, new_handler) 来设置对 SIGINT 信号的新处理函数 new_handler,那么你不能直接获取或查询在调用 signal 函数之前对 SIGINT 信号所设置的处理函数。这种情况下,你无法在程序中简单地查询或检查当前 SIGINT 信号的处理函数是什么。这可能导致一定的不确定性,特别是在多个模块设置信号处理函数或者需要了解之前的处理方式时。

3.2使用sigaction 函数

sigaction 是用于设置信号处理函数的高级函数,相较于 signal 函数,它提供了更多的控制和选项。通过 sigaction 函数,可以更精确地管理信号处理。
sigaction 定义如下:

#include <signal.h>

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

int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
  • signum是信号的编号
  • act是一个结构体 struct sigaction,包含新的信号处理函数,信号屏蔽集,以及标志位
  • oldact 是一个可选参数,用于存储之前的信号处理配置信息。

struct sigaction 结构体包含以下字段:

  • sa_handler:指定处理信号的函数,类似于 signal 函数中的处理函数。但与 signal 不同的是,sa_handler 允许设置为 SIG_DFL(默认操作)或 SIG_IGN(忽略信号)。
  • sa_sigaction:可选项,是带有更多信息的信号处理函数,用于处理信号和附加信息。如果设置了这个函数,sa_handler 就会被忽略。
  • sa_mask:用来设置在处理当前信号时需要屏蔽的信号集。
  • sa_flags:设置信号处理的一些标志位,例如 SA_RESTART 表示在某些系统调用中自动重启。
  • sa_restorer:一些特定系统用到的字段,一般无需设置。
    sigaction 函数的返回值为 0 表示成功,-1 表示失败。
    使用sigaction函数代码演示
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>

void sigint_handler(int sig) {
    printf("捕获到 SIGINT 信号\n");
    printf("处理 SIGINT 信号...\n");

    exit(0);
}

int main() {
    struct sigaction new_action, old_action;

    // 设置新的信号处理程序
    new_action.sa_handler = sigint_handler;
    sigemptyset(&new_action.sa_mask); // 清空信号屏蔽集
    new_action.sa_flags = 0;

    // 设置SIGINT信号的新处理程序
    if (sigaction(SIGINT, &new_action, &old_action) == -1) {
        perror("无法设置信号处理程序");
        return 1;
    }

    printf("按下 Ctrl+C 以发送 SIGINT 信号...\n");

    // 让程序保持运行,等待信号
    while (1) {
        sleep(1);
    }

    return 0;
}

在这里插入图片描述

4.alarm 和pause函数

4.1 alarm函数详解

#include <unistd.h>
unsigned int alarm(unsigned int seconds);

seconds 参数表示定时器的秒数。当定时器到达设定的秒数后,会发送 SIGALRM 信号给当前进程。
注意:返回值有点绕,函数的返回值为前一个定时器剩余的秒数。如果之前有设置过定时器,则返回之前定时器剩余的时间,如果之前没有设置定时器,则返回 0。
代码演示:

#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#define DELAY  5
void alarm_handler(int signum) {
    printf("捕获到 SIGALRM 信号 bye\n");
    exit(0);
}

int main() {
    signal(SIGALRM, alarm_handler); // 设置 SIGALRM 的处理函数
    unsigned int remaining_time = alarm(DELAY); // 设置定时器为5秒
    printf("alarm 返回值为 %u \n", remaining_time);   //这里的返回值其实为0 因为之前没有设置新的定时器只有一个的话,返回为0
    printf("等待%ds\n",DELAY);
    while(1) {
        // 程序的其他工作可以在这里执行
    
    sleep(DELAY);
    }
    return 0;
}

在这里插入图片描述
需要注意的是,alarm 函数设置的定时器是单次定时器,一旦定时器到时,就会被取消。如果需要重复定时功能,需要在 alarm_handler 函数中再次调用 alarm 来设置新的定时器。

4.2 pause函数详解

pause 函数是一个系统调用,用于使调用进程挂起直到收到一个信号。它通常用于程序中暂时等待某个信号的到来。

int pause(void);

pause 函数没有参数,调用它会使当前进程挂起,直到接收到一个信号为止。当进程接收到信号后,如果信号的默认处理方式是终止进程,那么进程将会终止。如果信号的默认处理方式是调用一个函数,那么进程会执行相应的信号处理函数,然后继续执行。通常,pause 函数用于让进程等待某个信号的到来,比如等待 SIGINT 或其他自定义信号。
代码演示:

#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
void sig_handler(int signum) {
    printf("捕获到自定义信号 %d\n", signum);
    if(signum==10){
    printf("执行退出命令\n");
    exit(0) ;
    }
    
    if(signum==12){
    printf("执行xxxx命令\n");
    }
    
}
    


int main() {
    signal(SIGUSR1, sig_handler); // 设置自定义信号的处理函数
    printf("等待自定义信号...\n");
    pause(); // 进程挂起等待自定义信号
    printf("自定义信号函数执行完毕,程序退出\n");
    return 0;
}

在终端编译运行之后,新开一个终端。可以用kill 发送自定义信号发送给进程。例如:

kill -s 12 5700

其中12 为自定义信号,Linux系统中一般10 或12 为自定义信号,5700为进程的pid。
可以使用ps -aux 查看进程pid
效果如下:
在这里插入图片描述

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

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

相关文章

1.4.1机器学习——梯度下降+α学习率大小判定

1.4.1梯度下降 4.1、梯度下降的概念 ※【总结一句话】&#xff1a;系统通过自动的调节参数w和b的值&#xff0c;得到最小的损失函数值J。 如下&#xff1a;是梯度下降的概念图。 我们有一个损失函数 J(w,b)&#xff0c;包含两个参数w和b&#xff08;你可以想象成J(w,b) w*x…

Windows环境下使用HTML5播放RTSP流

本文搭建环境选用Windows平台 一、使用笔记本摄像头推送RTSP视频流 参考文章:笔记本摄像头模拟监控推送RTSP流-CSDN博客 二、搭建Vue项目 参考项目&#xff1a; HTML5 播放 rtsp视频流 2.1 下载压缩包 2.2 解压文件&#xff0c;使用VSCode打开项目 2.3 启动项目 注&#…

vue day06

1、路由模块封装 2、声明式导航 实现导航高亮效果 直接通过这两个类名对相应标签设置样式 点击a链接进入my页面时&#xff0c;a链接 我的音乐高亮&#xff0c;同时my下的a、b页面中的 我的音乐也有router-link-active类&#xff0c;但没有精确匹配的类&#xff08;只有my页…

Talk | EMNLP 2023 最佳长论文:以标签为锚-从信息流动的视角分析上下文学习

本期为TechBeat人工智能社区第561期线上Talk。 北京时间1月4日(周四)20:00&#xff0c;北京大学博士生—王乐安的Talk已准时在TechBeat人工智能社区开播&#xff01; 他与大家分享的主题是: “以标签为锚-从信息流动的视角分析上下文学习”&#xff0c;介绍了他的团队在上下文学…

Python 安卓开发:Kivy、BeeWare、Flet、Flutter

kivy&#xff1a;https://github.com/kivy python-for-android &#xff1a;https://python-for-android.readthedocs.io/en/latest/ BeeWare&#xff1a;https://docs.beeware.org/en/latest/ Flet&#xff1a;https://github.com/flet-dev/flet 把 PySide6 移植到安卓上去&a…

1880_安装QEMU_for_ARC

Grey 全部学习内容汇总&#xff1a; https://github.com/GreyZhang/g_ARC 主标题 想学习一点ARC相关的知识&#xff0c;但是手里没有开发板。看了下&#xff0c;使用QEMU似乎是一个很好的选择&#xff0c;正好也有这么一个分支。在此&#xff0c;记录一下环境搭建的过程。 …

Java项目:01 springboot智能养生平台设计与实现

项目介绍 Java项目 智能养生平台 使用技术&#xff1a;springmybatisspringmvchtmlJavaScriptcsslayuijQuery 运行环境 jdk8mysqlIntelliJ IDEAmaven 主要分两个端&#xff0c;用户端和管理员端 网站功能&#xff1a;实现论坛帖子管理&#xff0c;论坛帖子分类管理&#xff0c…

计算机基础面试题 |20.精选计算机基础面试题

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

YOLOv5改进 | 2023主干篇 | EfficientViT替换Backbone(高效的视觉变换网络)

一、本文介绍 本文给大家带来的改进机制是EfficientViT(高效的视觉变换网络),EfficientViT的核心是一种轻量级的多尺度线性注意力模块,能够在只使用硬件高效操作的情况下实现全局感受野和多尺度学习。本文带来是2023年的最新版本的EfficientViT网络结构,论文题目是Effici…

4.MapReduce 序列化

目录 概述序列化序列化反序例化java自带的两种Serializable非Serializable hadoop序例化实践 分片/InputFormat & InputSplit日志 结束 概述 序列化是分布式计算中很重要的一环境&#xff0c;好的序列化方式&#xff0c;可以大大减少分布式计算中&#xff0c;网络传输的数…

推荐VSCODE插件:为`package.json`添加注释信息

众所周知&#xff0c;JSON文件是不支持注释的&#xff0c;除了JSON5/JSONC之外&#xff0c;我们在开发项目特别是前端项目时&#xff0c;大量会用到JSON文件&#xff0c;特别是在编写package.json中的scripts时&#xff0c;由于缺少注释,当有大量的命令脚本时&#xff0c;就有了…

Spring Cloud 介绍

文章目录 微服务技术栈Spring Cloud 介绍京东、阿里的微服务架构SpringBoot 和 SpringCloud 版本选择Springboot版本选择Springcloud版本选择Springcloud和Springboot之间的依赖关系如何看Spring Cloud 组件的升级替换 微服务技术栈 [toc] Spring Cloud 介绍 Spring Cloud是…

Element+vue3.0 tabel合并单元格span-method

Elementvue3.0 tabel合并单元格 span-method :span-method"objectSpanMethod"详解&#xff1a; 在 objectSpanMethod 方法中&#xff0c;rowspan 和 colspan 的值通常用来定义单元格的行跨度和列跨度。 一般来说&#xff0c;rowspan 和 colspan 的值应该是大于等于…

一氧化碳中毒悲剧频发:探究道合顺电化学传感器促进家庭取暖安全

1月6日&#xff0c;陕西省榆林市发生了一起疑似因使用煤炭炉取暖中毒事件。通报称&#xff0c;经公安部门现场调查&#xff0c;并结合医院救治情况&#xff0c;初步判断5人属一氧化碳中毒&#xff0c;其中4人抢救无效死亡&#xff0c;令人痛心。 一般来说&#xff0c;这种在日…

【System Verilog and UVM实力进阶1】SVA语法

毛主席说过&#xff1a;人不犯我我不犯人&#xff0c;人若犯我我必犯人。 目录 1 SVA介绍 1.1 什么是断言 1.2 为什么用System Verilog 断言&#xff08;SVA&#xff09; 1.3 System Verilog的调度 1.4 SVA术语 1.4.1 并发断言 1.4.2 即时断言 1.5 建立SVA块 1.6 一个简…

抖音矩阵云混剪系统源码 短视频矩阵营销系统V2.2.1(免授权版)

抖音矩阵云混剪系统源码 短视频矩阵营销系统V2.2.1&#xff08;免授权版&#xff09; 中网智达矩阵营销系统多平台多账号一站式管理&#xff0c;一键发布作品。智能标题&#xff0c;关键词优化&#xff0c;排名查询&#xff0c;混剪生成原创视频&#xff0c;账号分组&#xff…

基于Jackson自定义json数据的对象转换器

1、问题说明 后端数据表定义的id主键是Long类型&#xff0c;一共有20多位。 前端在接收到后端返回的json数据时&#xff0c;Long类型会默认当做数值类型进行处理。但前端处理20多位的数值会造成精度丢失&#xff0c;于是导致前端查询数据出现问题。 测试前端Long类型的代码 …

单机多卡训练报错NCCL版本有问题

torch.distributedtorch.distributed…DistBackendErrorDistBackendError: : NCCL error in: …/torch/csrc/distributed/c10d/ProcessGroupNCCL.cpp:1275, internal error, NCCL version 2.14.3 这个不知道什么原因&#xff0c;然后解决方法是 增加环境变量NCCL_SOCKET_IFNAM…

FreeRTOS——软件定时器

一、什么是定时器 简单可以理解为闹钟&#xff0c;到达指定一段时间后&#xff0c;就会响铃。 STM32 芯片自带硬件定时器&#xff0c;精度较高&#xff0c;达到定时时间后会触发中断&#xff0c;也可以生成 PWM 、输入捕获、输出 比较&#xff0c;等等&#xff0c;功能强大&a…

HarmonyOS应用开发学习笔记 应用上下文Context 获取文件夹路径

1、 HarmoryOS Ability页面的生命周期 2、 Component自定义组件 3、HarmonyOS 应用开发学习笔记 ets组件生命周期 4、HarmonyOS 应用开发学习笔记 ets组件样式定义 Styles装饰器&#xff1a;定义组件重用样式 Extend装饰器&#xff1a;定义扩展组件样式 5、HarmonyOS 应用开发…