【Linux】信号:产生信号

server/2025/3/24 3:04:29/

Alt

🔥个人主页Quitecoder

🔥专栏linux笔记仓

Alt

目录

    • 01.信号
      • 信号处理
      • 简单理解信号的发送和保存
      • 由软件产生的信号
      • 由硬件(异常)产生的信号

01.信号

进程信号是操作系统提供的一种异步通知机制,用于通知进程发生了某种事件

dyx@dyx-VMware-Virtual-Platform:~/bitcode$ kill -l1) SIGHUP       2) SIGINT       3) SIGQUIT      4) SIGILL       5) SIGTRAP6) SIGABRT      7) SIGBUS       8) SIGFPE       9) SIGKILL     10) SIGUSR1
11) SIGSEGV     12) SIGUSR2     13) SIGPIPE     14) SIGALRM     15) SIGTERM
16) SIGSTKFLT   17) SIGCHLD     18) SIGCONT     19) SIGSTOP     20) SIGTSTP
21) SIGTTIN     22) SIGTTOU     23) SIGURG      24) SIGXCPU     25) SIGXFSZ
26) SIGVTALRM   27) SIGPROF     28) SIGWINCH    29) SIGIO       30) SIGPWR
31) SIGSYS      34) SIGRTMIN    35) SIGRTMIN+1  36) SIGRTMIN+2  37) SIGRTMIN+3
38) SIGRTMIN+4  39) SIGRTMIN+5  40) SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+8
43) SIGRTMIN+9  44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-7
58) SIGRTMAX-6  59) SIGRTMAX-5  60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2
63) SIGRTMAX-1  64) SIGRTMAX

每个信号都有一个编号和一个宏定义名称,这些宏定义可以在signal.h中找到,例如其中有定 义 #define SIGINT 2

编号34以上的是实时信号

用户按下Ctrl-C ,这个键盘输入产生一个硬件中断,被OS获取,解释成信号,发送给目标前台进程
前台进程因为收到信号,进而引起进程退出

信号处理

信号处理(Signal Handling) 是指进程在接收到信号时,如何响应和处理该信号。信号处理的核心思想是通过 捕获信号 并执行 自定义的处理函数,从而改变信号的默认行为。以下是信号处理的详细解析。


信号处理的基本概念

  1. 默认行为
    • 每个信号都有一个默认行为(如终止进程、忽略信号、生成核心转储等)。
  2. 捕获信号
    • 进程可以捕获信号并执行自定义的处理函数,从而覆盖默认行为
  3. 信号处理函数
    • 也称为 信号处理器(Signal Handler),是一个用户定义的函数,用于处理特定信号。
  4. 异步性
    • 信号处理是异步的,即信号可能在任何时刻被接收并触发处理函数。
    man 7 signal查看实时信号的默认动作
    在这里插入图片描述
    有Core和Term终止的,也有Stop暂停的

信号处理的实现
在 Linux/UNIX 系统中,信号处理可以通过以下两种方式实现:

(1) signal 函数
功能:为指定信号设置处理函数。
原型

typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);

参数
signum:信号编号(如 SIGINT)。
handler:信号处理函数,或者 SIG_IGN(忽略信号)、SIG_DFL(恢复默认行为)。
返回值:成功返回之前的处理函数,失败返回 SIG_ERR
示例

#include<iostream>
#include<signal.h>
#include<unistd.h>using namespace std;
void hander(int sig)
{std::cout<<"get a sig"<<sig<<endl;
}
int main()
{//对信号自定义捕捉,只需要捕捉一次,后续一直有效signal(2,hander);while(true){cout<<"mypid:"<<getpid()<<endl;sleep(3);}return 0;
}

在这里插入图片描述

当进程接收到信号2时,会调用hander函数,而不是执行默认行为(默认行为是终止进程)

9号信号不允许自定义捕捉

信号产生:通过kill命令,向指定的进程发送指定的信号,键盘ctrl+c等可以产生信号,还有一些系统调用接口,真正发送信号的只有OS

在这里插入图片描述

简单理解信号的发送和保存

  1. 信号的发送

信号的发送是指一个进程(或内核)向另一个进程(或自身)发送信号的过程。信号的发送可以是同步的(如段错误)或异步的(如 Ctrl+C)。

信号发送的方式

  1. 用户发送
    • 通过键盘(如 Ctrl+C 发送 SIGINT)。
    • 使用 kill 命令(如 kill -9 <PID> 发送 SIGKILL)。
  2. 内核发送
    • 当发生某些事件时,内核会向进程发送信号。例如:
    SIGSEGV:段错误。
    SIGCHLD:子进程退出。
  3. 进程发送
    • 进程可以通过 kill 系统调用向其他进程发送信号。
    kill(pid, sig);
    

信号发送的时机
异步发送
• 信号可以在任何时候被发送,与目标进程的执行状态无关。
• 例如:Ctrl+C 发送 SIGINT
同步发送
• 信号是由目标进程的某些行为触发的。
• 例如:访问非法内存地址会触发 SIGSEGV

#include <signal.h>
#include <unistd.h>
#include <iostream>int main() {pid_t pid = getpid();kill(pid, SIGINT); // 向当前进程发送 SIGINT 信号return 0;
}

  1. 信号的保存

信号的保存是指操作系统如何管理已发送但尚未处理的信号。由于信号是异步的,进程可能正在执行关键代码,无法立即处理信号,因此需要将信号暂时保存起来。

信号的保存过程涉及task_struct 中的 blocked 和 pending 字段

(1) 信号保存的机制

  1. 信号掩码(Signal Mask)
    • 每个进程都有一个信号掩码,用于指定哪些信号被阻塞。
    • 被阻塞的信号会被挂起,直到解除阻塞。
  2. 挂起信号队列(Pending Signals)
    当一个信号被发送但被阻塞时,它会被放入挂起信号队列中
    • 当信号被解除阻塞时,操作系统会从队列中取出信号并交付给进程。

发送信号:修改指定进程pcb中信号的指定位图,0->1,写信号

int raise(int signo);

给当前进程自己发信号

abort函数使当前进程接收到信号而异常终止

#include <stdlib.h>
void abort(void);

由软件产生的信号

由软件产生的信号是通过操作系统或应用程序的逻辑触发的信号。这些信号通常用于通知进程某些特定的软件条件或事件,例如:
• 管道破裂(SIGPIPE)。
• 定时器到期(SIGALRM)。
• 子进程退出(SIGCHLD)。
• 用户发送的信号(如 kill 命令)。

信号描述
SIGPIPE当进程向一个已经关闭的管道或套接字写入数据时,内核会发送此信号。
SIGALRM当设置的定时器到期时,内核会发送此信号。
SIGCHLD当子进程终止或停止时,内核会发送此信号。
SIGUSR1用户自定义信号 1,通常用于进程间通信。
SIGUSR2用户自定义信号 2,通常用于进程间通信。
SIGTERM通常由 kill 命令发送,用于请求进程终止。
SIGINT通常由 Ctrl+C 发送,用于中断进程。

  1. SIGPIPE 信号
    触发条件当进程向一个已经关闭的管道或套接字写入数据时,内核会发送 SIGPIPE 信号。
    示例
#include <unistd.h>
#include <signal.h>
#include <iostream>void handler(int sig) {std::cout << "Received SIGPIPE: " << sig << std::endl;
}int main() {signal(SIGPIPE, handler);int fd[2];pipe(fd); // 创建管道close(fd[0]); // 关闭读端write(fd[1], "hello", 5); // 向已关闭的管道写入数据,触发 SIGPIPEreturn 0;
}
  1. SIGALRM 信号
#include <unistd.h>
unsigned int alarm(unsigned int seconds);

调用alarm函数可以设定一个闹钟,也就是告诉内核在seconds秒之后给当前进程发SIGALRM信号, 该信号的默认处理动作是终止当前进程
触发条件:当设置的定时器到期时,内核会发送 SIGALRM 信号。
示例

#include <unistd.h>
#include <signal.h>
#include <iostream>void handler(int sig) {std::cout << "Received SIGALRM: " << sig << std::endl;
}int main() {signal(SIGALRM, handler);alarm(2); // 设置 2 秒定时器pause(); // 等待信号return 0;
}
  1. SIGCHLD 信号
    触发条件:当子进程终止或停止时,内核会发送 SIGCHLD 信号。
    示例
#include <unistd.h>
#include <signal.h>
#include <iostream>void handler(int sig) {std::cout << "Received SIGCHLD: " << sig << std::endl;
}int main() {signal(SIGCHLD, handler);pid_t pid = fork();if (pid == 0) {// 子进程sleep(1);exit(0);} else {// 父进程pause(); // 等待信号}return 0;
}

由硬件(异常)产生的信号

在操作系统中,由硬件产生的信号 是指由硬件事件(如 CPU 异常、硬件中断等)触发的信号。这些信号通常用于通知进程发生了某种硬件相关的异常或事件。以下是硬件产生信号的详细解析:


  1. 硬件产生信号的来源

硬件产生的信号通常由以下事件触发:

(1) CPU 异常
示例
段错误(Segmentation Fault):当进程访问非法内存地址时,CPU 会触发 SIGSEGV 信号。
除零错误(Divide by Zero):当进程尝试除以零时,CPU 会触发 SIGFPE 信号。
非法指令(Illegal Instruction):当进程执行非法指令时,CPU 会触发 SIGILL 信号。

(2) 硬件中断
示例
时钟中断(Timer Interrupt):硬件定时器定期触发中断,操作系统利用这些中断实现调度和定时器功能。
键盘中断(Keyboard Interrupt):当用户按下键盘时,硬件会触发中断,操作系统可以将其转换为 SIGINT 信号(如 Ctrl+C)。

(3) 硬件故障
示例
总线错误(Bus Error):当进程访问未对齐的内存地址或硬件故障时,CPU 会触发 SIGBUS 信号。
硬件故障(Hardware Failure):某些硬件故障(如内存损坏)可能会触发信号。


  1. 常见的硬件产生的信号
信号描述
SIGSEGV当进程访问非法内存地址时,CPU 触发此信号。
SIGFPE当进程执行非法算术操作(如除以零)时,CPU 触发此信号。
SIGILL当进程执行非法指令时,CPU 触发此信号。
SIGBUS当进程访问未对齐的内存地址或硬件故障时,CPU 触发此信号。
SIGTRAP当进程遇到调试断点或陷阱指令时,CPU 触发此信号。
SIGABRT当进程调用 abort 函数时,CPU 触发此信号。

硬件产生信号的示例

(1) SIGSEGV 信号
触发条件:进程访问非法内存地址。
示例代码

#include <signal.h>
#include <iostream>void handler(int sig) {std::cout << "Received SIGSEGV: " << sig << std::endl;exit(1);
}int main() {signal(SIGSEGV, handler);int *ptr = nullptr;*ptr = 42; // 访问非法内存地址,触发 SIGSEGVreturn 0;
}

(2) SIGFPE 信号
触发条件:进程执行非法算术操作(如除以零)。
示例代码

#include <signal.h>
#include <iostream>void handler(int sig) {std::cout << "Received SIGFPE: " << sig << std::endl;exit(1);
}int main() {signal(SIGFPE, handler);int a = 0;int b = 1 / a; // 除以零,触发 SIGFPEreturn 0;
}

(3) SIGILL 信号
触发条件:进程执行非法指令。
示例代码

#include <signal.h>
#include <iostream>void handler(int sig) {std::cout << "Received SIGILL: " << sig << std::endl;exit(1);
}int main() {signal(SIGILL, handler);asm volatile ("ud2"); // 执行非法指令,触发 SIGILLreturn 0;
}

进程崩溃了会默认退出,捕捉异常信号就可以不退出

操作系统如何知道进程出了异常?

​CPU 异常:当进程执行非法操作(如访问非法内存地址、除以零、执行非法指令等)时,CPU 会检测到异常,CPU 会触发相应的 ​异常处理机制,将控制权交给操作系统=,操作系统将硬件异常转换为信号发送给发生异常的进程

CPU 寄存器 在异常检测和处理中起到了关键作用:

  • 程序计数器(PC)记录触发异常的指令地址。
  • 状态寄存器(PSW)记录异常的类型和状态。
  • 栈指针寄存器(SP)保存异常发生时的上下文

http://www.ppmy.cn/server/177195.html

相关文章

《Python实战进阶》No26: CI/CD 流水线:GitHub Actions 与 Jenkins 集成

No26: CI/CD 流水线&#xff1a;GitHub Actions 与 Jenkins 集成 摘要 持续集成&#xff08;CI&#xff09;和持续部署&#xff08;CD&#xff09;是现代软件开发中不可或缺的实践&#xff0c;能够显著提升开发效率、减少错误并加速交付流程。本文将探讨如何利用 GitHub Actio…

Android Compose 框架文本组件模块源码深度剖析(一)

一、引言 在 Android 开发中&#xff0c;文本显示是最基础且常用的功能之一。Android Compose 作为新一代的声明式 UI 工具包&#xff0c;为开发者提供了简洁、高效且功能强大的文本组件。通过深入分析 Compose 框架中文本组件模块的源码&#xff0c;我们可以更好地理解其工作…

Elasticsearch基础

核心概念 一、倒排索引原理 倒排索引又叫反向索引(inverted index)&#xff0c;既然有反向索引那就有正向索引(forward index)了。 正向索引&#xff1a;当用户发起查询时&#xff08;假设查询为一个关键词&#xff09;&#xff0c;搜索引擎会扫描索引库中的所有文档&#x…

github如何为开源项目作出贡献

就在昨天&#xff0c;笔者取得了第一次开源项目贡献&#xff0c;虽然更新的内容很小&#xff0c;但是也算是迈出了第一步 1. 选择合适的开源项目 &#xff08;1&#xff09;兴趣优先 选择自己感兴趣的项目会更有动力参与&#xff0c;比如你喜欢前端开发&#xff0c;可以关注…

【 <二> 丹方改良:Spring 时代的 JavaWeb】之 Spring Boot 的起步依赖:快速构建 JavaWeb 项目

<前文回顾> 点击此处查看 合集 https://blog.csdn.net/foyodesigner/category_12907601.html?fromshareblogcolumn&sharetypeblogcolumn&sharerId12907601&sharereferPC&sharesourceFoyoDesigner&sharefromfrom_link <今日更新> 一、起步依赖…

机器人曲面跟踪Surface-Tracking

定义 机器人曲面跟踪&#xff08;Surface-Tracking&#xff09;是指机器人通过实时感知工件曲面的三维形貌&#xff0c;动态调整运动轨迹和位姿&#xff0c;以精确跟随曲面进行加工&#xff08;如打磨、抛光、喷涂等&#xff09;的技术。 力 - 位姿协同控制 力控模式&#xff…

个人陈述本人于2011年8月被XXX大学经济学专业录取

本人于2011年8月被XXX大学经济学专业录取。在三年的学习中&#xff0c;我渐渐领略到了经济学的独特魅力&#xff0c;对经济学产生了浓厚的兴趣。秉着专一学习态度&#xff0c;不断向着目标努力&#xff0c;我取得了优秀的成绩&#xff0c;前五学期总成绩91.08分&#xff0c;名列…

【Leetcode 每日一题】2680. 最大或值

问题背景 给你一个下标从 0 0 0 开始长度为 n n n 的整数数组 n u m s nums nums 和一个整数 k k k。每一次操作中&#xff0c;你可以选择一个数并将它乘 2 2 2。 你最多可以进行 k k k 次操作&#xff0c;请你返回 n u m s [ 0 ] ∣ n u m s [ 1 ] ∣ . . . ∣ n u m …