嘿,小伙伴们!今天我要和大家聊一个Linux系统中非常有趣又重要的话题——信号机制。别担心,虽然信号听起来有点高深,但我会用最通俗易懂的语言,配合清晰的图表,带你彻底搞懂这个概念!

什么是信号?

想象一下,如果你正在专心写代码,突然有人拍了一下你的肩膀,这就类似于操作系统中的"信号"。信号是Linux系统中用于通知进程发生了某种事件的一种异步通信机制,就像操作系统给进程发送的"紧急短信"。

信号的本质是软件中断,当进程收到信号后,会暂停当前工作,转而去处理这个信号,处理完后再回到原来的工作。这就像你接到一个紧急电话,处理完紧急事务后再回到之前的工作一样。

为什么需要信号?

在Linux系统中,信号主要用于以下几个场景:

  1. 错误处理:当程序出现严重错误(如除零、非法内存访问)时,系统会发送相应信号
  2. 终止进程:用户可以通过按下Ctrl+C发送SIGINT信号来终止前台进程
  3. 进程间通信:一个进程可以通过信号通知另一个进程发生了某事
  4. 定时器功能:通过SIGALRM信号实现定时器功能
  5. 状态变化通知:如子进程终止时,父进程会收到SIGCHLD信号

Linux信号的种类

Linux系统定义了多种信号,每种信号都有特定的用途。以下是一些常见的信号:

信号名称信号值默认动作描述
SIGHUP1终止终端断开连接
SIGINT2终止键盘中断(Ctrl+C)
SIGQUIT3终止 + core键盘退出(Ctrl+\)
SIGILL4终止 + core非法指令
SIGTRAP5终止 + core断点陷阱
SIGABRT6终止 + core调用 abort 函数
SIGFPE8终止 + core浮点异常
SIGKILL9终止强制终止(不可捕获)
SIGSEGV11终止 + core段错误(无效内存引用)
SIGPIPE13终止管道破裂
SIGALRM14终止定时器到期
SIGTERM15终止终止信号(kill 命令默认)
SIGUSR110终止用户自定义信号 1
SIGUSR212终止用户自定义信号 2
SIGCHLD17忽略子进程状态改变
SIGCONT18继续继续执行被停止的进程
SIGSTOP19停止停止进程(不可捕获)
SIGTSTP20停止键盘停止(Ctrl+Z)

信号的生命周期

信号的生命周期包括三个阶段:产生、未决和处理。

1. 信号的产生

信号可以通过多种方式产生:

2. 信号的未决状态

当信号产生后,会进入未决状态,等待被处理。如果此时该信号被阻塞(blocked),则会保持未决状态,直到解除阻塞。

3. 信号的处理

当信号递达(delivered)到进程后,进程会根据信号处理方式来响应:

  • 默认处理:每个信号都有默认动作,如终止进程、忽略信号等
  • 忽略信号:进程可以选择忽略某些信号(但SIGKILL和SIGSTOP不能被忽略)
  • 捕获信号:进程可以注册自定义的信号处理函数

信号处理的编程实践

注册信号处理函数

在C/C++中,我们可以使用signal()或更强大的sigaction()函数来注册信号处理函数:

#include <signal.h>// 信号处理函数void signal_handler(int signum) {printf("捕获到信号 %d\n", signum);// 处理信号的代码}int main() {// 注册SIGINT信号的处理函数signal(SIGINT, signal_handler);// 程序主循环while(1) {printf("程序运行中...\n");sleep(1);}return 0;}

使用sigaction()函数(推荐)

sigaction()比signal()更强大,提供了更多控制选项:

#include <signal.h>void signal_handler(int signum) {printf("捕获到信号 %d\n", signum);}int main() {struct sigaction sa;sa.sa_handler = signal_handler;sigemptyset(&sa.sa_mask);  // 清空信号集sa.sa_flags = 0;// 注册SIGINT信号的处理函数sigaction(SIGINT, &sa, NULL);while(1) {printf("程序运行中...\n");sleep(1);}return 0;}

发送信号

进程可以使用kill()函数向其他进程发送信号:

#include <signal.h>#include <sys/types.h>int main() {pid_t pid = 1234;  // 目标进程ID// 向进程发送SIGTERM信号kill(pid, SIGTERM);return 0;}

信号传递流程图:

信号集操作

信号集是一组信号的集合,可以用来表示要阻塞的信号。Linux提供了一系列函数来操作信号集:

#include <signal.h>int main() {sigset_t set;// 初始化信号集sigemptyset(&set);  // 清空信号集// 添加信号到集合sigaddset(&set, SIGINT);sigaddset(&set, SIGTERM);// 阻塞这些信号sigprocmask(SIG_BLOCK, &set, NULL);// ... 执行不想被这些信号打断的代码 ...// 解除阻塞sigprocmask(SIG_UNBLOCK, &set, NULL);return 0;}

实际应用场景

1. 优雅地退出程序

当用户按下Ctrl+C时,我们可能需要先清理资源再退出:

#include <signal.h>#include <stdio.h>#include <stdlib.h>#include <unistd.h>volatile sig_atomic_t keep_running = 1;void cleanup_and_exit() {printf("清理资源...\n");// 关闭文件、释放内存等清理操作printf("清理完成,退出程序\n");}void handle_sigint(int sig) {printf("\n捕获到SIGINT信号\n");keep_running = 0;}int main() {struct sigaction sa;sa.sa_handler = handle_sigint;sigemptyset(&sa.sa_mask);sa.sa_flags = 0;sigaction(SIGINT, &sa, NULL);printf("程序开始运行,按Ctrl+C退出\n");while (keep_running) {printf("工作中...\n");sleep(1);}cleanup_and_exit();return 0;}

2. 父进程监控子进程

父进程可以通过SIGCHLD信号来监控子进程的状态变化:

#include <signal.h>#include <stdio.h>#include <stdlib.h>#include <sys/types.h>#include <sys/wait.h>#include <unistd.h>void handle_sigchld(int sig) {int status;pid_t pid;// 非阻塞方式等待任何子进程while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {if (WIFEXITED(status)) {printf("子进程 %d 正常退出,退出码: %d\n", pid, WEXITSTATUS(status));} else if (WIFSIGNALED(status)) {printf("子进程 %d 被信号 %d 终止\n", pid, WTERMSIG(status));}}}int main() {struct sigaction sa;sa.sa_handler = handle_sigchld;sigemptyset(&sa.sa_mask);sa.sa_flags = SA_RESTART;sigaction(SIGCHLD, &sa, NULL);// 创建子进程pid_t pid = fork();if (pid < 0) {perror("fork");exit(1);} else if (pid == 0) {// 子进程printf("子进程 %d 开始运行\n", getpid());sleep(2);printf("子进程 %d 结束运行\n", getpid());exit(42);} else {// 父进程printf("父进程 %d 创建了子进程 %d\n", getpid(), pid);// 父进程继续执行其他工作for (int i = 0; i < 5; i++) {printf("父进程工作中...\n");sleep(1);}}return 0;}

3. 使用定时器

通过SIGALRM信号实现定时功能:

#include <signal.h>#include <stdio.h>#include <unistd.h>void handle_alarm(int sig) {printf("时间到!\n");}int main() {struct sigaction sa;sa.sa_handler = handle_alarm;sigemptyset(&sa.sa_mask);sa.sa_flags = 0;sigaction(SIGALRM, &sa, NULL);printf("设置3秒定时器...\n");alarm(3);printf("等待定时器...\n");pause();  // 暂停直到收到信号printf("继续执行\n");return 0;}

信号处理的注意事项

  1. 信号处理函数应该尽量简单:因为信号处理函数可能在任何时候被调用,所以应该避免复杂操作。
  2. 不可重入函数:在信号处理函数中应避免调用不可重入函数(如malloc、printf等),可能导致不可预测的行为。
  3. 全局变量访问:如果在信号处理函数和主程序之间共享变量,应声明为volatile sig_atomic_t类型,确保原子访问。
  4. SIGKILL和SIGSTOP:这两个信号不能被捕获、阻塞或忽略,始终执行默认动作。
  5. 信号丢失:如果同一信号多次发送,而进程还没来得及处理,通常只会记录一次,可能导致信号丢失。

信号与多线程

在多线程程序中,信号处理变得更加复杂:

  1. 信号会被发送到进程中的任一线程,由系统选择
  2. 可以使用pthread_sigmask()函数来设置线程的信号掩码
  3. 可以使用sigwait()函数来专门处理信号的线程
#include <signal.h>#include <pthread.h>#include <stdio.h>#include <unistd.h>void* signal_thread(void* arg) {sigset_t* set = (sigset_t*)arg;int sig;while (1) {// 等待信号sigwait(set, &sig);printf("收到信号 %d\n", sig);if (sig == SIGINT) {printf("处理SIGINT信号\n");} else if (sig == SIGTERM) {printf("处理SIGTERM信号,准备退出\n");break;}}return NULL;}int main() {sigset_t set;pthread_t thread;// 初始化信号集sigemptyset(&set);sigaddset(&set, SIGINT);sigaddset(&set, SIGTERM);// 在主线程中阻塞这些信号pthread_sigmask(SIG_BLOCK, &set, NULL);// 创建专门处理信号的线程pthread_create(&thread, NULL, signal_thread, &set);printf("主线程运行中,按Ctrl+C发送SIGINT,kill -15 %d发送SIGTERM\n", getpid());// 主线程继续工作while (1) {printf("主线程工作中...\n");sleep(1);}pthread_join(thread, NULL);return 0;}

小结

信号是Linux系统中一种重要的进程间通信机制,虽然功能相对简单(只能传递信号类型,不能传递额外数据),但在系统编程中有着广泛的应用。掌握信号处理,对于编写健壮的Linux程序至关重要。

信号机制看似简单,实则暗藏玄机,特别是在多线程环境下。作为一名C++开发工程师,我建议大家在实际项目中谨慎使用信号,遵循最佳实践,避免常见陷阱。

希望这篇文章能帮助你理解Linux信号机制!如果有问题,欢迎在评论区留言交流~


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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Vue3项目引入高德地图【超详细教程】

前言 在 Vue 3 项目中集成高德地图&#xff08;AMap&#xff09;是一个常见的需求。本文将详细介绍如何在 Vue 3 项目中使用高德地图&#xff0c;包括安装配置、基本使用以及一些进阶功能的实现。 一、环境准备 1.1 vue3项目初始化 步骤 1&#xff1a;初始化项目 npm crea…

blender mcp安装(完全免费的ai建模)

1.最关键的一步&#xff0c;建议最早执行(就是安装uvx) mac系统执行 brew install uvwindows执行 powershell -c "irm https://astral.sh/uv/install.ps1 | iex" 出现这一步就成功安装uvx了&#xff0c;因为mcp需要使用uvx 2.第二步骤 github地址: https://gith…

GIS开发入门教程与笔记分享

大家好&#xff0c;我是地信小学生&#xff0c;距离5月3日发布暂停更新以来&#xff0c;也一两个月啦&#xff0c;这期间也陆陆续续更新了点内容。 我自己写的笔记主要是以入门笔记为主&#xff0c;真正的内容并不多&#xff0c;包括&#xff1a;GIS基础、PostgreSQLPostGIS入门…

设计模式-代理模式、装饰者模式

代理模式 Proxy&#xff08;代理&#xff09;—对象结构型模式定义&#xff1a;给某一个对象提供一个代理对象&#xff0c;并由代理对象控制原有对象的引用。 代理模式的核心思想是&#xff1a;创建一个代理对象&#xff0c;代理对象在调用目标方法时&#xff0c;可以插入额外…

国产安路FPGA纯verilog视频图像去雾,基于暗通道先验算法实现,提供5套TD工程源码和技术支持

目录 1、前言工程概述免责声明 2、相关方案推荐我已有的所有工程源码总目录----方便你快速找到自己喜欢的项目国产安路FPGA相关方案推荐本博主已有的图像处理方案 3、设计思路框架工程设计原理框图输入Sensor之-->GC0308摄像头输入Sensor之-->OV7725摄像头输入Sensor之--…

Windows商店中的简笔画学习应用

此应用包含动物、植物、人物、交通工具、卡通等类别超过1500张线条图片&#xff0c;支持图片临摹和图片填色&#xff0c;可以将绘图和填色结果保存成文件&#xff0c;也可以打开本地图片进行临摹和填色。 菜单说明 右侧绘图区上方菜单功能包括&#xff1a;打开文件&#xff1…

树莓派4B --ubundu20.04 机载电脑配置WIFI热点

不要用刷机过程配置WIFI账号&#xff0c;因为在那里配置的WIFI都是不受控的&#xff0c;会出很多问题。 1.安装网络 sudo apt-get install network-manager 2.将源码CLONE到本地 sudo git clone https://github.com/oblique/create_ap cd create_ap sudo make install 当你…

​​JETSON NANO B01​ 在AIOT 的领域的作用

低功耗边缘设备的理想选择 &#x1f449; ​​适合人群​​&#xff1a;精打细算、小厂搞智能监控的 ​​Jetson Nano B01​​&#xff08;4GB内存/0.47TOPS算力&#xff09;&#xff0c;JetBot (NVIDIA社区版) 机器人/自动驾驶项目​ ​​硬件​​&#xff1a;Jetson Nano B0…

Kioptrix Level2

靶机截图 收集信息 主机发现 打开靶机后&#xff0c;用kali探测靶机的 IP arp-scan-l 可以用nmap进行同网段扫描探测存活ip nmap -sP 10.4.7.0/24 端口扫描 命令过程 nmap -sT -sV -p- -O 10.4.7.220 -sT&#xff1a;TCP连接扫描 -sV&#xff1a;服务版本探测 -p-&#x…

Word之电子章制作——1

第一步&#xff1a;在插入 ——形状哪里选择一个圆形&#xff0c;并且下一步按住shift键拉出一个正圆形。 第二步&#xff1a;鼠标右键去掉背景颜色&#xff0c;边框粗细设置成3磅。 第三步&#xff1a;在插入导航窗找到艺术字&#xff0c;点击大写的A&#xff0c;输入公司名字…

LeetCode 2799.统计完全子数组的数目

给你一个由 正 整数组成的数组 nums 。 如果数组中的某个子数组满足下述条件&#xff0c;则称之为 完全子数组 &#xff1a; 子数组中 不同 元素的数目等于整个数组不同元素的数目。 返回数组中 完全子数组 的数目。 子数组 是数组中的一个连续非空序列。 示例 1&#xff1…

33.表复制和去重

1.表结构的复制(LIKE) 当我们想复制一个表的时候&#xff0c;首先需要创建一个与被复制表相同结构的表。这时候就要用到关键字like&#xff1a; 语法使用&#xff1a; create table table_name LIKE temp_table 示例&#xff1a;复制一个和表emp&#xff08;老朋友了&#…

GitLab 18.1 正式发布Maven 虚拟仓库、密码泄露检测等功能,可升级体验!

GitLab 是一个全球知名的一体化 DevOps 平台&#xff0c;很多人都通过私有化部署 GitLab 来进行源代码托管。极狐GitLab 是 GitLab 在中国的发行版&#xff0c;专门为中国程序员服务。可以一键式部署极狐GitLab。 学习极狐GitLab 的相关资料&#xff1a; 极狐GitLab 官网极狐…

蓝牙网络拓扑面试通关:微微网与散射网原理 + 真题解析

为什么面试官总爱问蓝牙拓扑? 你可能有过这样的经历:面试嵌入式 / 物联网 / 无线通信岗位时,面试官突然问:“蓝牙的微微网和散射网有什么区别?” 别慌!这不是在考你背定义,而是考察你对无线通信核心逻辑的理解 ——如何用有限资源实现高效组网。 蓝牙作为短距离无线通信…

[Python]-基础篇1- 从零开始的Python入门指南

无论你是尚未接触编程的新手,还是想从其他语言转向Python的开发者,这篇文章都是你的入门课。 一、Python是什么? Python是一种解释型、高级、通用型编程语言,以简洁明了、简单易用着称。它可以应用于网站开发、自动化脚本、数据分析、人工智能、系统操作等多种场景。 二、…

Objective-C面向对象编程:类、对象、方法详解(保姆级教程)

目录 一、核心概念 二、类的定义&#xff08;分.h和.m文件&#xff09; 1. 头文件&#xff08;.h&#xff09;—— 公开声明 2. 实现文件&#xff08;.m&#xff09;—— 具体实现 3. 属性特性解析 原子性 所有权语义(ARC环境下) 读写控制 三、对象创建与内存管理 1…

CentOS 7 编译ClickHouse 24.8完整指南

前言 在CentOS 7上编译ClickHouse 24.8可能会遇到一些挑战&#xff0c;主要是因为CentOS 7的默认软件版本较旧。本文将详细介绍从零开始构建ClickHouse 24.8的完整过程&#xff0c;包括依赖安装和环境配置。 准备工作 首先确保系统已更新到最新版本&#xff1a; yum update…

Protocol Buffers (Protobuf) 全面解析

一、核心概念解析 1. 什么是数据序列化&#xff1f; #mermaid-svg-HZKw9iRlpQIRFiO3 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-HZKw9iRlpQIRFiO3 .error-icon{fill:#552222;}#mermaid-svg-HZKw9iRlpQIRFiO3 .…

高斯混合模型GMMK均值(十三-1)——K均值是高斯混合模型的特例

EM算法与K均值算法的关系 K均值可以看成是高斯混合模型的特例。 对K均值算法与EM算法进行比较后&#xff0c;可以发现它们之间有很大的相似性。K均值算法将数据点硬&#xff08;hard&#xff09;分配到聚类中&#xff0c;每个数据点唯一地与一个聚类相关联&#xff0c;而EM算法…

StarRocks 向量索引如何让大模型“记性更好”?

随着 ChatGPT、DeepSeek 等大语言模型的普及&#xff0c;我们已经能够与 AI 进行流畅的对话。然而&#xff0c;即使是最先进的大模型也面临着“记忆困境”&#xff0c;具体表现模型只能记住训练时接触的知识&#xff0c;且这些知识在使用时很可能会过期。实际应用或在处理特定领…