Linux 信号机制

Linux 信号机制

    • 一、Linux信号的基本概念
    • 二、怎么理解Linux信号机制
    • 三、Linux信号的用途
    • 四、Linux信号的原理
    • 五、Linux信号的处理方法
      • 1、默认处理
      • 2、忽略信号
      • 3、自定义信号处理
    • 六、Linux信号处理的细节
      • 1、信号掩码
      • 2、实时信号
      • 3、Linux信号 Alarm
    • 七、Linux信号处理库函数
      • 1. signal() 函数
        • 函数原型:
        • 示例代码:
      • 2. sigaction() 函数
        • 函数原型:
        • 示例代码:
      • 3. sigprocmask() 函数
        • 函数原型:
        • 示例代码:
      • 4. kill() 函数
        • 函数原型:
        • 示例代码:
    • 八、Linux信号列表

Linux信号是操作系统中进程间通信的一种重要机制,信号提供了一种简洁而强大的方式,用于通知进程发生了某些事件或需要执行特定的操作

一、Linux信号的基本概念

信号是一种异步的、软件中断的机制,用于通知进程某些事件的发生。进程接收到信号时,会中断其正常执行,并转而执行信号处理函数,处理完信号后再继续执行。信号主要用于进程之间的通信和控制。

每个信号在内核中都有唯一的编号,并且每种信号都有特定的意义。例如,SIGTERM请求进程正常终止,SIGKILL强制终止进程,SIGSEGV表示段错误等。

二、怎么理解Linux信号机制

Linux信号机制是一种轻量级的进程间通信方式。它的设计初衷是让进程能够快速响应操作系统或其他进程发出的事件通知。信号的发送和接收通常不依赖于共享内存或复杂的同步机制,因此它比其他通信方式(如消息队列、共享内存等)更加简单和高效。

当一个进程接收到信号时,它的执行会被中断,操作系统会根据信号类型来决定如何处理这个信号。信号机制主要由两部分组成:信号发送和信号处理。

三、Linux信号的用途

信号的用途非常广泛,主要用于以下几个方面:

  1. 进程控制:信号可用于控制进程的执行。例如,SIGSTOP可以暂停进程,SIGCONT可以恢复进程的执行,SIGTERM用于请求进程正常终止。

  2. 进程间通信:进程可以通过发送信号来通知另一个进程某些事件的发生。例如,父进程可以通过SIGCHLD信号接收到子进程的状态信息。

  3. 错误处理和异常通知:信号常用于报告程序中的错误或异常,如SIGSEGV表示段错误,SIGFPE表示浮点运算错误。

  4. 定时任务和超时管理:信号还可用于定时任务,例如使用SIGALRM来设置定时器,一旦超时就会触发指定的信号。

四、Linux信号的原理

Linux信号的原理基于异步通知的概念。每当一个进程接收到信号时,内核会中断进程的当前操作,跳转到对应的信号处理函数或执行默认行为。信号本身并不包含任何数据,除了某些特定类型的信号(如实时信号)可以携带附加数据外。

信号的发送是通过内核的kill()系统调用实现的。一个进程可以向另一个进程发送信号,进程根据信号类型来执行相应的操作。如果信号的目标进程正在执行某些任务,进程的执行会被中断。

信号的发送过程通常包括以下几个步骤:

  1. 信号队列:每个进程都有一个信号队列,用于存储接收到的信号。
  2. 信号调度:当进程进入就绪状态时,内核会调度信号的处理。
  3. 信号处理:进程可以选择捕获信号并执行自定义的处理,或者直接使用默认的信号处理行为。

五、Linux信号的处理方法

Linux信号处理主要有三种方式:默认处理、忽略信号和自定义处理

1、默认处理

每种信号都有一个默认的处理方式。例如,SIGTERM信号会导致进程退出,SIGKILL会强制终止进程,SIGSTOP会暂停进程。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>int main() {printf("This is a demo program. Press Ctrl+C or send SIGTERM to terminate.\n");while(1) {sleep(1);  // 模拟进程持续运行}
}

在这个示例中,默认行为是接收到SIGTERM信号时进程退出。

2、忽略信号

进程可以选择忽略某些信号。例如,SIGINT信号通常由Ctrl+C触发,我们可以选择忽略它:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>void signal_handler(int sig) {// 忽略信号并不执行任何动作
}int main() {signal(SIGINT, signal_handler);  // 忽略 SIGINT 信号printf("Signal SIGINT is now ignored. Press Ctrl+C, but nothing will happen.\n");while(1) {sleep(1);}
}

3、自定义信号处理

进程可以定义一个函数来处理接收到的信号,并在该函数中执行特定的操作。例如,处理SIGTERM信号时执行资源清理:

#include <stdio.h>
#include <unistd.h>
#include <signal.h>void cleanup(int signal) {printf("Custom handler: Received SIGTERM. Performing cleanup.\n");// 执行清理操作// exit(0);
}int main() {signal(SIGTERM, cleanup);  // 捕获 SIGTERM 信号printf("Waiting for SIGTERM signal...\n");while(1) {sleep(1);  // 模拟进程持续运行}
}

六、Linux信号处理的细节

1、信号掩码

信号掩码(Signal Mask)用于阻止某些信号在进程中被捕获,尤其在多线程环境下,信号掩码可以控制哪些线程能够接收某些信号。例如,进程可以屏蔽SIGINT信号:

#include <signal.h>
#include <stdio.h>int main() {sigset_t set;sigemptyset(&set);sigaddset(&set, SIGINT);sigprocmask(SIG_BLOCK, &set, NULL);  // 屏蔽 SIGINT 信号printf("SIGINT is blocked. Press Ctrl+C, but it won't be processed.\n");while(1) {sleep(1);}return 0;
}

2、实时信号

实时信号(SIGRTMINSIGRTMAX)是Linux提供的一类特殊信号,它们允许携带更多的附加数据,且具有较高的优先级。实时信号不与标准信号冲突,通常用于需要精确控制的进程间通信。

3、Linux信号 Alarm

SIGALRM信号用于定时器,进程可以设置一个定时器,当定时器超时后触发该信号。通常用于超时处理或定时任务。

#include <stdio.h>
#include <signal.h>
#include <unistd.h>void alarm_handler(int sig) {printf("Received SIGALRM: Timer expired!\n");
}int main() {signal(SIGALRM, alarm_handler);alarm(5);  // 设置一个5秒的定时器printf("Waiting for alarm...\n");while(1) {sleep(1);}return 0;
}

七、Linux信号处理库函数

在Linux中,信号处理是通过一系列系统调用和库函数实现的。常见的信号处理库函数有:signal()sigaction()sigprocmask()kill()

1. signal() 函数

signal()函数用于设置信号的处理函数。当进程接收到特定信号时,会调用对应的处理函数。

函数原型:
void (*signal(int signum, void (*handler)(int)))(int);
  • signum:指定信号的编号(如SIGINTSIGTERM等)。
  • handler:指定一个处理该信号的函数。当信号到达时,系统会调用这个函数。
示例代码:
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>void signal_handler(int sig) {printf("Received signal: %d\n", sig);exit(0);  // 处理完信号后退出
}int main() {signal(SIGINT, signal_handler);  // 捕获Ctrl+C (SIGINT)printf("Waiting for SIGINT...\n");while (1) {sleep(1);  // 持续运行}return 0;
}

在此例中,进程通过signal()函数捕获SIGINT信号(通常由Ctrl+C触发)。当接收到SIGINT时,程序调用signal_handler()函数处理信号,并退出。

2. sigaction() 函数

sigaction()是一个更为强大且灵活的信号处理函数,比signal()提供了更多的控制选项。sigaction()可以指定信号处理函数,并允许设置信号处理的其他行为,如信号掩码、信号的处理动作等。

函数原型:
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
  • signum:指定信号的编号。
  • act:指定新的信号处理结构体,包含了信号处理函数和其他行为。
  • oldact:如果非NULL,保存原来的信号处理方式。
示例代码:
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>void sigaction_handler(int sig) {printf("Received signal: %d\n", sig);exit(0);
}int main() {struct sigaction sa;sa.sa_handler = sigaction_handler;sigemptyset(&sa.sa_mask);  // 不屏蔽任何信号sa.sa_flags = 0;  // 使用默认行为sigaction(SIGINT, &sa, NULL);  // 捕获SIGINT信号printf("Waiting for SIGINT...\n");while (1) {sleep(1);}return 0;
}

在这个示例中,我们使用sigaction()来捕获SIGINT信号,并通过自定义的sigaction_handler()函数处理信号。sigemptyset()用于清空信号掩码,表示在处理信号时不阻塞任何其他信号。

3. sigprocmask() 函数

sigprocmask()用于控制进程的信号掩码。信号掩码指定了哪些信号在进程中被阻塞,即不允许被处理。

函数原型:
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
  • how:指定信号掩码的操作方式,常用的值有:
    • SIG_BLOCK:阻塞信号。
    • SIG_UNBLOCK:解除信号阻塞。
    • SIG_SETMASK:将信号掩码设置为给定的set
  • set:指定信号集,用于设置或修改信号掩码。
  • oldset:如果非NULL,保存旧的信号掩码。
示例代码:
#include <stdio.h>
#include <signal.h>
#include <unistd.h>int main() {sigset_t new_mask, old_mask;sigemptyset(&new_mask);sigaddset(&new_mask, SIGINT);  // 将SIGINT加入到信号集sigprocmask(SIG_BLOCK, &new_mask, &old_mask);  // 阻塞SIGINT信号printf("SIGINT is blocked. Press Ctrl+C, but it won't be processed.\n");sleep(5);  // 休眠5秒钟sigprocmask(SIG_SETMASK, &old_mask, NULL);  // 恢复原信号掩码printf("SIGINT is now unblocked. You can press Ctrl+C.\n");while (1) {sleep(1);}return 0;
}

在这个示例中,SIGINT信号被阻塞,直到信号掩码恢复为原来的状态,SIGINT才会被处理。

4. kill() 函数

kill()函数用于向指定进程发送信号,触发信号处理。

函数原型:
int kill(pid_t pid, int sig);
  • pid:目标进程的PID。可以是指定进程的ID,或者一些特殊值(如0表示向进程组发送信号,-1表示向所有进程发送信号)。
  • sig:指定要发送的信号类型。
示例代码:
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>int main() {pid_t pid = getpid();  // 获取当前进程IDprintf("Sending SIGTERM to process with PID %d...\n", pid);if (kill(pid, SIGTERM) == -1) {perror("Error sending SIGTERM");}return 0;
}

此示例向当前进程发送SIGTERM信号,要求其终止。


八、Linux信号列表

信号名称信号编号说明
SIGHUP1挂起信号,通常用于终端断开或重新加载配置文件
SIGINT2中断信号,通常由Ctrl+C触发
SIGQUIT3退出信号,进程会生成核心转储文件(core dump)
SIGILL4非法指令信号,进程执行非法指令时触发
SIGABRT6异常终止信号,通常由abort()触发
SIGFPE8浮点异常信号,如除以零或浮点溢出
SIGKILL9强制终止信号,无法被捕获或忽略
SIGSEGV11段错误信号,访问非法内存时触发
SIGPIPE13管道破裂信号,向一个没有读取端的管道写数据时触发
SIGALRM14定时器信号,用于超时或定时任务
SIGTERM15终止信号,通常用于请求进程优雅退出
SIGUSR110用户定义信号1,用户可以自定义的信号
SIGUSR212用户定义信号2,用户可以自定义的信号
SIGCHLD17子进程状态改变信号,通常用于父进程监控子进程的退出
SIGSTOP19暂停信号,暂停进程的执行,不能被捕获或忽略
SIGCONT18恢复信号,恢复一个被暂停的进程

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

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

相关文章

ChatGPT提问技巧:行业热门应用提示词案例--咨询法律知识

ChatGPT除了可以协助办公&#xff0c;写作文案和生成短视频脚本外&#xff0c;和还可以做为一个法律工具&#xff0c;当用户面临一些法律知识盲点时&#xff0c;可以向ChatGPT咨询获得解答。赋予ChatGPT专家的身份&#xff0c;用户能够得到较为满意的解答。 1.咨询法律知识 举…

mysql 学习8 函数,字符串函数,数值函数,日期函数,流程函数

函数 一 字符串函数 二 数值函数 三 日期函数 四 流程函数

机器学习--1.KNN机器学习入门

1、机器学习概述 1.1、什么是机器学习 机器学习&#xff08;Machine Learning&#xff09;是人工智能&#xff08;Artificial Intelligence&#xff09;领域的一个子集&#xff0c;它主要关注如何让计算机系统通过经验学习&#xff08;数据&#xff09;并自动改进性能。机器学…

【数据结构】(4) 线性表 List

一、什么是线性表 线性表就是 n 个相同类型元素的有限序列&#xff0c;每一个元素只有一个前驱和后继&#xff08;除了第一个和最后一个元素&#xff09;。 数据结构中&#xff0c;常见的线性表有&#xff1a;顺序表、链表、栈、队列。 二、什么是 List List 是 Java 中的线性…

go-zero学习笔记(三)

利用goctl生成rpc服务 编写proto文件 // 声明 proto 使用的语法版本 syntax "proto3";// proto 包名 package demoRpc;// golang 包名(可选) option go_package "./demo";// 如需为 .proto 文件添加注释&#xff0c;请使用 C/C 样式的 // 和 /* ... */…

深入浅出:频谱掩码 Spectral Masking —— 噪音消除利器

在语音处理领域&#xff0c;噪声是一个常见的敌人。无论是语音通话、语音识别&#xff0c;还是语音合成&#xff0c;噪声都会大大降低语音的质量和可理解性。为了解决这个问题&#xff0c;Spectral Masking&#xff08;频谱掩码&#xff09; 模型应运而生。它通过从带噪信号的频…

LeetCode --- 434周赛

目录 3432. 统计元素和差值为偶数的分区方案 3433. 统计用户被提及情况 3434. 子数组操作后的最大频率 3435. 最短公共超序列的字母出现频率 一、统计元素和差值为偶数的分区方案 本题可以直接模拟&#xff0c;当然我们也可以来从数学的角度来分析一下这题的本质 设 S S S …

如何安全地管理Spring Boot项目中的敏感配置信息

在开发Spring Boot应用时&#xff0c;我们经常需要处理一些敏感的配置信息&#xff0c;比如数据库密码、API密钥等。以下是一个最佳实践方案&#xff1a; 1. 创建配置文件 application.yml&#xff08;版本控制&#xff09; spring:datasource:url: ${MYSQL_URL:jdbc:mysql…

我主编的电子技术实验手册(24)——RL并联电路

本专栏是笔者主编教材&#xff08;图0所示&#xff09;的电子版&#xff0c;依托简易的元器件和仪表安排了30多个实验&#xff0c;主要面向经费不太充足的中高职院校。每个实验都安排了必不可少的【预习知识】&#xff0c;精心设计的【实验步骤】&#xff0c;全面丰富的【思考习…

【贪心算法篇】:“贪心”之旅--算法练习题中的智慧与策略(三)

✨感谢您阅读本篇文章&#xff0c;文章内容是个人学习笔记的整理&#xff0c;如果哪里有误的话还请您指正噢✨ ✨ 个人主页&#xff1a;余辉zmh–CSDN博客 ✨ 文章所属专栏&#xff1a;贪心算法篇–CSDN博客 文章目录 前言例题1.最优除法2.跳跃游戏23.跳跃游戏14.加油站5.单调递…

2024年度十大网络安全热点事件盘点:时代暗涌下的安全危机

2024年&#xff0c;国际形势风云变幻&#xff0c;地缘政治的动荡与科技革命的浪潮交织成一幅复杂图景。以人工智能为代表的前沿科技突飞猛进&#xff0c;正以前所未有的速度重塑着世界的每一个角落&#xff0c;引领着人类社会迈向一个更加智能、高效与便捷的未来。 然而&#…

【教程】禁止网页右键和打开调试页面

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhagn.cn] 如果本文帮助到了你&#xff0c;欢迎[点赞、收藏、关注]哦~ 普通页面&#xff0c;可以右键&#xff0c;并打开调试页面。不安全&#xff1a; 在网页中添加一下JavaScript代码&#xff0c;可以禁止网页右键和打开调…

使用开源项目:pdf2docx,让PDF转换为Word

pdf2docx&#xff1a;GitCode - 全球开发者的开源社区,开源代码托管平台 环境&#xff1a;windows电脑 1.安装python Download Python | Python.org 最好下载3.8以上的版本 安装时记得选择上&#xff1a;Add ... Path 安装时默认会装pip等工具&#xff0c;因此下载安装包时…

电控---中断

中断 1.处理器系统在执行代码的时候&#xff0c;会从存储器依次取出指令和数据&#xff0c;这种能力需要在处理器里保存一个存储器地址&#xff0c;就是所谓的程序计数器&#xff08;Program Counter,PC&#xff09;&#xff0c;也叫程序指针 2.当外部中断&#xff08;Extern …

编程AI深度实战:大模型哪个好? Mistral vs Qwen vs Deepseek vs Llama

​​ 系列文章&#xff1a; 编程AI深度实战&#xff1a;私有模型deep seek r1&#xff0c;必会ollama-CSDN博客 编程AI深度实战&#xff1a;自己的AI&#xff0c;必会LangChain-CSDN博客 编程AI深度实战&#xff1a;给vim装上AI-CSDN博客 编程AI深度实战&#xff1a;火的编…

计算机视觉-边缘检测

一、边缘 1.1 边缘的类型 ①实体上的边缘 ②深度上的边缘 ③符号的边缘 ④阴影产生的边缘 不同任务关注的边缘不一样 1.2 提取边缘 突变-求导&#xff08;求导也是一种卷积&#xff09; 近似&#xff0c;1&#xff08;右边的一个值-自己可以用卷积做&#xff09; 该点f(x,y)…

【高级篇 / IPv6】(7.2) ❀ 05. 在60E上配置ADSL拨号宽带上网(IPv6) ❀ FortiGate 防火墙

【简介】上一篇文章了解了如何在60E上配置ADSL拨号IPv4上网&#xff0c;这篇文章将介绍如何在FortiGate上配置IPv6&#xff0c;使得内网电脑都能以IPv6地址上网。 启用IPv6 由于IPv6比较小众&#xff0c;默认在防火墙是看不到的。 ① 接上一篇文章&#xff0c;由于Internal已经…

可视化大屏在石油方面的应用。

可视化大屏通过整合石油工业全链条数据&#xff0c;构建数字孪生驱动的运营监控体系&#xff0c;显著提升油气勘探、开采、储运及炼化的管理效能。其技术架构依托工业物联网&#xff08;IIoT&#xff09;实时采集钻井参数、管道压力、储罐液位等数据&#xff0c;通过OPC UA协议…

Vim的基础命令

移动光标 H(左) J(上) K(下) L(右) $ 表示移动到光标所在行的行尾&#xff0c; ^ 表示移动到光标所在行的行首的第一个非空白字符。 0 表示移动到光标所在行的行首。 W 光标向前跳转一个单词 w光标向前跳转一个单词 B光标向后跳转一个单词 b光标向后跳转一个单词 G 移动光标到…

modbus协议处理

//------------------------0x01-------------------------------- //MDA_usart_send: aa 55 01 00 06 00 02 00 05 //转modbusTCP——Master——send&#xff1a;地址00002&#xff0c;寄存器数量&#xff1a;00005 00 00 00 00 00 06 01 01 00 02 00 05 //ModbusTCP——Slave…