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

文章目录

  • 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.mfbz.cn/a/306441.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

CDN的介绍以及加速内容传输原理

目前在公司的开发过程中&#xff0c;发现很多存储在oss的静态资源&#xff08;图片&#xff0c;安装包&#xff09;的链接中域名都使用了cdn域名&#xff0c;后面了解到这个cdn域名的主要作用是加速资源的访问&#xff0c;于是抽空了解了一下CDN加速原理。 目前使用得比较多的是…

CCSC,一种CPU架构

core-circuit-separate-computer 核与执行电路的分离&#xff0c;最初是为了省电。 用寄存器实现这种分离。 V寄存器控制着执行电路的供电&#xff0c;V0则不供电&#xff0c;进入省电模式&#xff1b;V1则供电&#xff0c;进入工作模式。 P寄存器是parameter-register&#xf…

3. SPSS数据文件的基本加工和处理

如何获取SPSS自带的案例数据文件&#xff1f; 首先找到SPSS的安装目录&#xff0c;然后找到Samples文件夹 可以看到有不同语言版本&#xff0c;选择简体中文 就能看到很多.sav文件 数据文件的整理 个案排序 单值排序 例&#xff1a;对于下面的数据集&#xff0c;将工资按…

【leetcode 2707. 字符串中的额外字符】动态规划 字典树

2707. 字符串中的额外字符 题目描述 给你一个下标从 0 开始的字符串 s 和一个单词字典 dictionary 。你需要将 s 分割成若干个 互不重叠 的子字符串&#xff0c;每个子字符串都在 dictionary 中出现过。s 中可能会有一些 额外的字符 不在任何子字符串中。 请你采取最优策略分割…

Developer Tools for Game Creator 1

插件包含: 持久世界时间管理系统 单击以生成对象或预设 游戏内调试控制台 游戏内事件控制台 控制台管理控制 命令模板脚本 游戏内屏幕截图 低分辨率和高分辨率图像 缩略图生成 移动支持 使用Game Creator Action或拖放来激活和控制组件,无需编码。 通过此资产,您可以获得: …

ESP32_ADC(Arduino)

ADC模数转换 ESP32集成了12位的逐次逼近式ADC&#xff0c;分别为ADC1模块ADC2模块&#xff0c;共支持18个模拟输入通道: ADC1模块&#xff1a;8个通道&#xff0c;32~39ADC2模块&#xff1a;10个通道&#xff0c;0&#xff0c;2&#xff0c;4&#xff0c;12 ~ 15&#xff0c;…

Linux网络配置概述

目录 一.查看网络配置 1.ifconfig 2.ip a 3.hostname 4.route 5.netstat和ss &#xff08;1&#xff09;netstat &#xff08;2&#xff09;ss &#xff08;3&#xff09;区别 6.ping 7.traceroute 8.nslookup 9.dig 二.网卡配置 三.域名解析配置文件 1.文件所…

用React给XXL-JOB开发一个新皮肤(一):环境搭建和项目初始化

目录 一. 简述二. Fork 项目三. 搭建开发环境四. 初始化皮肤项目五. 添加相关依赖六. 预览 一. 简述 大名鼎鼎的 xxl-job 任务调度中心我们应该都使用过&#xff0c;项目地址&#xff1a;xxl-job。它是一个分布式任务调度平台&#xff0c;其核心设计目标是开发迅速、学习简单…

【MySQL】导出导入(两种方式),远程备份

目录 前言&#xff1a; 一 navcat导入导出 1.1 导入 1.2 导出 二 mysqldump 导入导出 2.1 导入 2.2 导出 三 load data infile命令导入导出 3.1 导入 3.2 导出 四 远程备份 五 思维导图 前言&#xff1a; 随着当今企业发展&#xff0c;数据库的数据越来越多&…

vulhub中的Nginx漏洞的详细解析

Nginx漏洞 1.cd到nginx_parsing_vulnerability cd /opt/vulhub/nginx/nginx_parsing_vulnerability 2.执行docker-compose up -d 3.查看靶场是否开启成功 dooker ps 4.访问浏览器 因为这里是80端口所以直接使用ip就能访问成功 5.上传图片 注意这里的图片是含有一句话木马的图…

【大数据架构】日志采集方案对比

整体架构 日志采集端 Flume Flume的设计宗旨是向Hadoop集群批量导入基于事件的海量数据。系统中最核心的角色是agent&#xff0c;Flume采集系统就是由一个个agent所连接起来形成。每一个agent相当于一个数据传递员&#xff0c;内部有三个组件&#xff1a; source: 采集源&…

回顾 | AI 浪潮下的创业故事(一)—— Jina AI

点击蓝字 关注我们 编辑&#xff1a;Alan Wang 排版&#xff1a;Rani Sun 微软 Reactor 为帮助广开发者&#xff0c;技术爱好者&#xff0c;更好的学习 .NET Core, C#, Python&#xff0c;数据科学&#xff0c;机器学习&#xff0c;AI&#xff0c;区块链, IoT 等技术&#xff0…

论文阅读1---OpenCalib论文阅读之factory calibration模块

前言 该论文的标定间比较高端&#xff0c;一旦四轮定位后&#xff0c;可确定标定板与车辆姿态。以下为本人理解&#xff0c;仅供参考。 工厂标定&#xff0c;可理解为车辆相关的标定&#xff0c;不涉及传感器间标定 该标定工具不依赖opencv&#xff1b;产线长度一般2.5米 Fa…

SpiderFlow爬虫平台 前台RCE漏洞复现(CVE-2024-0195)

0x01 产品简介 SpiderFlow是新一代爬虫平台,以图形化方式定义爬虫流程,以流程图的方式定义爬虫,不写代码即可完成爬虫,是一个高度灵活可配置的爬虫平台。 0x02 漏洞概述 SpiderFlow爬虫平台src/main/java/org/spiderflow/controller/FunctionController.java文件的Functi…

CMake入门教程【核心篇】设置和使用缓存变量

😈「CSDN主页」:传送门 😈「Bilibil首页」:传送门 😈「动动你的小手」:点赞👍收藏⭐️评论📝 文章目录 概述设置缓存变量使用缓存变量更改缓存变量完整代码示例实战使用技巧注意事项总结与分析

python第三方模块之yaml模块

安装: pip install PyYamlPyYaml 5.1之后,通过禁止默认加载程序(FullLoader)执行任意功能,该load函数也变得更加安全。 使用: config.yaml - 10 - 20 - 30 - 40 - 50 --- name: cc age:<

关于git使用的tips

前言 这里是一些git指令使用的tips&#xff0c;如果你作为初学者的话&#xff0c;我认为它将对你有所帮助。 常见指令 常见问题处理 1、使用git clone下载【huggingface.co】资源超时或无法请求问题 绝大多数情况是网络问题&#xff0c;首先如果是比较大的资源&#xff0c;你需…

计算机网络-各层协议

大家在搞嵌入式开发的时候基本都了解过七层网络协议、五层网络协议、四层网络协议&#xff0c;那么今天让我们更加的深入了解一下&#xff1a; 历史发展介绍 OSI七层模型由ISO国际标准化组织提出的通信标准。TCP/IP四层模型是OSI七层模型的简化版&#xff0c;OSI在它被官方完…

vue-vben-admin 与.net core 结合实例 【自学与教学 小白教程】---第3节

ue-vben-admin 与.net core 结合实例 这里计划使用.net core 作为后端 。目标&#xff1a;打造好看 易用 开箱即用 的netcore一体化框架。Vue Vben Admin For NetCore 取命 hcrain-vvadmin 我不是前端人员 但有时开发还是要写一些界面。 之前使用layui是时候 狠心升级下了。 …

苹果电脑Markdown文本编辑Typora mac功能介绍

Typora mac是一款跨平台的Markdown编辑器&#xff0c;支持Windows、MacOS和Linux操作系统。它具有实时预览功能&#xff0c;能够自动将Markdown文本转换为漂亮的排版效果&#xff0c;让用户专注于写作内容而不必关心格式调整。Typora Mac版除了支持常见的Markdown语法外&#x…
最新文章