Linux:进程信号(一)信号的产生

news2024/10/7 12:28:10

目录

一、信号是什么?

二、Linux信号

 三、信号处理方式

四、信号的产生

1、 通过终端按键产生信号

 2、调用系统函数向进程发信号

3、 硬件异常产生信号


一、信号是什么?

        在生活中,有许多信号,比如红绿灯,下课铃声,而我们接收到这些信号,就会做出相应的处理,而有时候接收到信号后也不是立即处理,可能会过段时间处理。我们提前知道了该如何处理对应的信号,比如绿灯过马路,同样的,进程提前内置了信号识别功能。

        信号是一种进程间通信机制,用于通知进程发生了特定事件。这些事件可以是来自内核的通知(例如,进程已经终止)或者其他进程发送的信号。信号可以被用来实现进程之间的同步、中断处理、错误处理等。

需要注意的是:信号是异步的,信号是进程之间事件异步通知的一种方式,属于软中断。

信号是异步的,这意味着进程可以在任何时间接收到信号,而不是在特定的时间点或代码位置。当信号发生时,操作系统会中断进程的正常执行,将控制权交给信号处理函数(如果有),然后在处理完信号后恢复进程的执行。

因此,进程在执行期间可能会突然被信号中断,而不是在特定的同步点。这也意味着编写信号处理函数时需要格外小心,因为处理函数可能会在任何时候执行,并且需要考虑信号处理函数的安全性和可重入性。

信号的特点: 

延时性:信号的处理可能会受到一定程度的延迟,这取决于操作系统的调度和处理机制。虽然在用户层看来延时不易察觉,但对于某些实时性要求高的应用可能会产生影响。

异步通信:信号是一种异步通信方式,进程可以在任何时间接收到信号,而不需要在特定的同步点等待。

用户空间和内核空间交互:信号是用户空间和内核空间之间进行交互的重要机制,内核可以通过发送信号来通知用户空间进程发生了特定的系统事件,从而实现进程间的通信和协作。

这些特性使得信号成为操作系统中重要的进程间通信方式之一,能够实现进程之间的同步、事件处理、中断处理等功能。

二、Linux信号

使用kill -l 可以查看系统定义的信号列表 

下面是一些常用信号的功能:

  1. SIGINT (2):终端中断,通常由用户在终端中按下Ctrl+C发送,用于中止进程执行。

  2. SIGILL (4):非法指令,表示进程执行了非法的机器指令。

  3. SIGFPE (8):浮点异常,表示进程执行了一个不合法的浮点运算,如除以零或产生了溢出。

  4. SIGSEGV (11):段错误,表示进程访问了无效的内存地址。

  5. SIGTERM (15):终止请求,通常用于请求进程正常终止。

  6. SIGKILL (9):强制终止,无法被捕获或忽略的信号,立即终止目标进程。

  7. SIGHUP (1):终端挂起,通常表示与终端的连接断开。

  8. SIGPIPE (13):管道破裂,表示进程尝试向已关闭的管道写入数据。

  9. SIGALRM (14):定时器超时,表示定时器已经超时。

  10. SIGUSR1 (10)SIGUSR2 (12):用户定义信号,可以由用户进程自定义使用。

 可以使用man 7 signal来查看信号对应的处理动作

信号四要素:

信号编号(Signal Number):每个信号都有一个唯一的编号,用来标识特定的事件或异常。这些编号通常是整数,如SIGINT的编号是2。

信号名称(Signal Name):每个信号都有一个可读性的名称,用于方便人类理解。例如,SIGINT表示终端中断,SIGILL表示非法指令。

信号处理器(Signal Handler):信号处理器是一个函数,用来处理接收到的信号。进程可以为特定的信号设置自定义的处理函数,当接收到该信号时,系统会调用相应的处理函数来执行特定的操作。

默认动作(Default Action):每个信号都有一个默认的处理动作,用于在进程没有为信号设置自定义处理器时执行。常见的默认动作包括终止进程(如SIGKILL、SIGSEGV)、终止进程并生成核心转储文件(如SIGQUIT)、终止进程并忽略(如SIGPIPE)等。

这是关于信号默认动作的一些常见描述:

  • Term(终止):默认动作是终止进程。
  • Ign(忽略):默认动作是忽略该信号,即进程收到此信号时不采取任何动作。
  • Core(生成核心转储):默认动作是终止进程并生成核心转储文件,核心转储文件可以帮助调试进程崩溃的原因。
  • Stop(停止):默认动作是停止进程,进程会暂时停止执行直到接收到继续执行的信号。
  • Cont(继续):默认动作是在进程当前处于停止状态时继续执行进程。

 三、信号处理方式

  1. 默认处理方式:每个信号都有一个默认的处理方式,当进程接收到信号时,操作系统会按照该信号的默认处理方式来处理。比如,对于SIGINT信号,默认处理方式是终止进程。

  2. 信号忽略:进程可以选择忽略特定信号,即当接收到该信号时,不采取任何操作。这通常通过设置信号处理函数为SIG_IGN来实现。

  3. 自定义信号处理函数:进程可以注册自定义的信号处理函数,以便在接收到特定信号时执行自定义的处理逻辑。这通常通过调用signal()或者更现代的sigaction()函数来设置信号处理函数。

我们来看一下signal()函数的用法:

#include <stdio.h>
#include <signal.h>
//可以自定义处理的函数
void sigint_handler(int sig) {
    printf("Caught SIGINT, exiting...\n");
    // 这里可以执行一些清理工作
    exit(0);
}

int main() {
    // 注册SIGINT信号处理函数 
    signal(SIGINT, sigint_handler);

    printf("Waiting for SIGINT...\n");
    while(1) {
        // 等待信号
    }

    return 0;
}

这种方式称为捕捉 (Catch)一个信号,同理可以设置成别的信号对应的处理函数,但是有的信号虽然允许被捕捉,但是依旧会终止,比如6号信号。

SIGKILL (9):强制终止信号,无法被捕捉、阻塞或忽略。这个信号是用来立即终止进程的,无论进程的状态如何,一般用于处理僵尸进程或者无法正常终止的进程。

SIGSTOP (19)SIGCONT (18):停止和继续信号。SIGSTOP 用于暂停进程的执行,而 SIGCONT 则用于恢复进程的执行。这两个信号也不能被捕捉。

SIGABRT (6):异常终止信号。通常表示进程调用了abort函数,用于异常终止进程。

四、信号的产生

信号主要有三种产生方式:

1、 通过终端按键产生信号

我们来运行下列代码:

#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
void sigint_handler(int sig) {
    printf("接收到%d号信号\n",sig);
    exit(0);
}

int main() {
    // 注册SIGINT信号处理函数
    signal(SIGINT, sigint_handler);
    while(1) {
        // 等待信号
        printf("Waiting for SIGINT...\n");
    }

    return 0;
}

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

当按下Ctrl +z,会发出暂停

Ctrl+Z键组合会将前台作业挂起(暂停),然后可以使用fg命令将其恢复到前台运行

按下Ctrl + \  发出3号信号会终止进程,我们来验证一下啊

这个时候在运行时按下Ctrl + \ 

操作系统怎么知道键盘中有数据读入?

操作系统通常会通过一个称为中断请求(IRQ)的机制来检测键盘是否有数据输入。当用户按下键盘上的键时,键盘控制器会产生一个中断请求,通知处理器有数据输入。处理器收到中断请求后,会暂停当前的任务,转而执行与键盘中断相关的中断服务程序(ISR)。

中断服务程序会读取键盘控制器的状态寄存器,以确定键盘是否有数据输入。如果有数据输入,它会读取键盘缓冲区中的数据,并将其传输到操作系统的键盘缓冲区中。一旦数据被传输到操作系统,操作系统就可以从缓冲区中读取数据,以响应用户的键盘输入。

总之,操作系统通过中断请求机制来检测键盘是否有数据输入,并通过相应的中断服务程序来处理键盘输入数据

 2、调用系统函数向进程发信号

系统调用是指进程通过操作系统提供的接口请求某种服务或资源,这可能会导致某些信号的产生。

我们运行这段代码:

#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

void sigint_handler(int sig) {
    printf("接收到%d号信号\n",sig);
    exit(0);
}

int main() {
    // 注册SIGINT信号处理函数
    signal(2, sigint_handler);
    while(1) {
        // 等待信号
        printf("Waiting for SIGINT...我是%d进程\n",getpid());
        sleep(1);
    }

    return 0;
}

运行的同时使用kill命令向该进程发出9号信号

 

可以看到进程被杀掉,需要注意9号信号不可被捕捉

除了kill可以发送信号,raise同样可以发送信号:

raise可以给自己发信号,如果 raise() 函数成功发送信号,它将返回0;如果发生错误,则返回非零值。运行下面的代码:

#include <stdio.h>
#include <signal.h>
#include <unistd.h>

void sigint_handler(int sig) {
    printf("接收到信号 %d\n", sig);
}

int main() {
    // 注册信号处理程序
    signal(SIGINT, sigint_handler);

    printf("使用 raise() 函数发送 SIGINT 信号\n");
    // 发送 SIGINT 信号
    if (raise(SIGINT) != 0) {
        perror("raise() 失败");
    }

    printf("结束程序\n");

    return 0;
}

abort()也可以发出信号

abort() 函数也可以发出信号。当调用 abort() 函数时,它会向调用进程发送 SIGABRT 信号,这会导致程序异常终止,并且系统通常会生成一个 core dump 文件,用于调试。


#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include<stdlib.h>
void sigint_handler(int sig) {
    printf("接收到信号 %d\n", sig);
}

int main() {
    // 注册信号处理程序
    signal(SIGABRT, sigint_handler);

    while(1)
    {
        abort();
    }

    printf("结束程序\n");

    return 0;
}

运行后发现虽然abort()允许被捕捉,但是依旧会终止。

3、 硬件异常产生信号

OS可以把CPU内部硬件错误解释成信号

硬件异常被硬件以某种方式被硬件检测到并通知内核 , 然后内核向当前进程发送适当的信号。
例如当前进程执行了除以0 的指令,CPU的运算单元会产生异常,内核将这个异常解释 为SIGFPE信号发送给进程。再比如当前进程访问了非法内存地址,,MMU会产生异常,内核将这个异常解释为SIGSEGV信号发送给进程。

常见的硬件异常导致的信号包括:

段错误(SIGSEGV):段错误是最常见的硬件异常之一,它通常由进程试图访问未授权的内存地址引起。这个信号通常表示程序在尝试读写一个未分配的内存地址,或者违反了内存访问权限。

非法指令(SIGILL):这个信号通常由进程试图执行无法识别或未授权的机器指令引起。非法指令可能是由于代码损坏或不兼容的机器指令集导致的。

浮点异常(SIGFPE):这个信号通常由程序在执行算术运算时出现异常(如除以零、溢出、下溢或无效运算)而引发。

总线错误(SIGBUS):这个信号通常由进程尝试对未对齐的内存地址进行访问或尝试访问不存在的物理内存导致。

我们来模拟一下浮点异常(SIGFPE)

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include<stdlib.h>
void sigint_handler(int sig) {
    printf("接收到信号 %d\n", sig);
    sleep(1);
}
void  Exception()
{
    int x=0;
    int a=4/x;
}
int main() {
    // 注册信号处理程序
    signal(SIGFPE, sigint_handler);
    Exception();
    return 0;
}

可以看到浮点异常(SIGFPE)对应的是8号信号

但是捕捉到异常后我们的sigint_handler函数并没有处理,如果不处理就不会改变异常,所以就会一直发信号,所以尽量在捕捉后进行处理。

小结

以上就是一些信号的产生方式,但是需要记住无论信号有多少钟,永远都只能让操作系统向目标进程发送,因为操作系统是进程的管理者。

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

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

相关文章

达梦主从数据库实例恢复

测试环境&#xff1a;实时主备数据库 1、在节点1向测试表aaa插入数据 如图可见&#xff0c;会话139695153554808向aaa表插入了10000行数据。事务id460520。 2、提交前在另一个窗口kill掉dmserver进程。 3、查看节点2的数据库日志 上图可见&#xff0c;系统执行alter database…

Java基础教程 - 4 流程控制

更好的阅读体验&#xff1a;点这里 &#xff08; www.doubibiji.com &#xff09; 更好的阅读体验&#xff1a;点这里 &#xff08; www.doubibiji.com &#xff09; 更好的阅读体验&#xff1a;点这里 &#xff08; www.doubibiji.com &#xff09; 4 流程控制 4.1 分支结构…

在企业中软件产品测试报告可以运用的场景

在企业应用场景中&#xff0c;测试报告的应用场景十分广泛且重要。以下是几个主要的应用场景&#xff1a; 产品质量评估与保证&#xff1a;测试报告是企业评估软件或产品质量的重要依据。通过测试报告&#xff0c;企业可以了解产品在不同场景下的性能表现、安全性、稳定性以及…

esp32-cam 1. 出厂固件编译与测试

0. 环境 - ubuntu18 - esp32-cam - usb转ttl ch340 硬件连接 esp32-camch340板子U0RTXDU0TRXDGNDGND5V5V 1. 安装依赖 sudo apt-get install vim sudo apt install git sudo apt-get install git wget flex bison gperf python python-pip python-setuptools python-serial p…

Redis的数据类型及使用场景

redis命令大全官网: Commands | Docs (redis.io) 基本介绍 redis起初主要就是为了解决性能问题的&#xff0c;那么redis为什么快? 基于内存操作的&#xff0c;所以操作不需要跟磁盘进行交互&#xff0c;单次的执行会很快 命令执行是单线程 因为基于内存操作 单次执行时间反…

Vue开发者工具Vue.js devtools Vue开发者工具安装步骤前端开发工具免费附带教程

下载地址&#xff1a; 链接: https://pan.baidu.com/s/1JaGvhS4NoD8lL07n2ScE9A 密码: 9rfs 安装步骤&#xff1a; 以谷歌浏览器为例 第一步&#xff1a;打开Chrome的拓展程序 如图 第二步&#xff1a; 将下载好的拓展程序拖入即可&#xff0c;如下图 第三步&#xff1a;…

Python数据清洗与可视化实践:国际旅游收入数据分析

文章目录 概要整体流程名词解释NumPyPandasMatplotlibre 技术细节数据清洗可视化 小结 概要 在本篇博客中&#xff0c;我们将通过一个实际的案例&#xff0c;演示如何使用Python进行数据清洗和可视化&#xff0c;以分析国际旅游收入数据。我们将使用Python中的Pandas库来进行数…

OpenHarmony实战开发-应用侧调用前端页面函数

应用侧可以通过runJavaScript()方法调用前端页面的JavaScript相关函数。 在下面的示例中&#xff0c;点击应用侧的“runJavaScript”按钮时&#xff0c;来触发前端页面的htmlTest()方法。 前端页面代码。 <!-- index.html --> <!DOCTYPE html> <html> <…

接口自动化框架篇:Pytest + Allure报告企业定制化实现!

接口自动化框架是现代软件开发中的重要组成部分&#xff0c;能够帮助开发团队提高测试效率和质量。本文将介绍如何使用Pytest作为测试框架&#xff0c;并结合Allure报告进行企业定制化实现。 目标规划 在开始编写接口自动化测试框架之前&#xff0c;我们需要先进行目标规划。…

超分辨率重建——BSRN网络训练自己数据集并推理测试(详细图文教程)

目录 一、BSRN网络总结二、源码包准备三、环境准备3.1 报错KeyError: "No object named BSRN found in arch registry!"3.2 安装basicsr源码包3.3 参考环境 四、数据集准备五、训练5.1 配置文件参数修改5.2 启动训练5.2.1 命令方式训练5.2.2 配置Configuration方式训…

zTasker v1.88.1一键定时自动化任务

软件介绍 zTasker是一款完全免费支持定时、热键或条件触发的方式执行多种自动化任务的小工具&#xff0c;支持win7-11。其支持超过100种任务类型&#xff0c;50种定时/条件执行方法&#xff0c;而且任务列表可以随意编辑、排列、移动、更改类型&#xff0c;支持任务执行日志&a…

SEED-X:多模态智能助手

SEED-X&#xff1a;多模态智能助手 SEED-X 是一个多模态智能助手&#xff0c;已经将所有的模型和代码开源了&#xff01;它是一个统一且多用途的多模态基础模型&#xff0c;最新开放了图像编辑模型。 相较于传统的多模态交互框架&#xff0c;SEED-X 具有以下优点&#xff1a;…

【请投票】嘉立创EDA中LED发光二极管是否应有统一的引脚定义?

LED发光二极管的引脚定义应该是唯一的吗&#xff1f; 从下面原理图可以看到&#xff0c;器件型号仅尾缀不同,R代表RED红色发光二极管&#xff0c;W代表WHITE指白色发光二极管&#xff0c;是同一家制造商KENTO&#xff0c; 左侧红色发光二极管的1脚是阴极K&#xff0c;2脚是阳极…

Android关于SparseArray面试题

问题1: 什么是SparseArray&#xff0c;它与HashMap有什么不同&#xff1f; 回答&#xff1a; SparseArray是一个用于优化特定情况下内存使用的数据结构&#xff0c;主要用于替代HashMap<Integer, Object>。SparseArray使用两个数组分别存储键和值&#xff0c;而不是使用…

初识Linux -- Linux的背景和发展史介绍

点赞关注不迷路&#xff01;&#xff0c;本节涉及初识Linux&#xff0c;主要为背景介绍和xshell登录主机。 1.Linux背景 1.1 发展史 Linux从哪里来&#xff1f;它是怎么发展的&#xff1f;在这里简要介绍Linux的发展史。 要说Linux&#xff0c;还得从UNIX说起。 1.2 UNIX发…

【Linux】掌握Linux系统编程中的权限与访问控制

&#x1f49e;&#x1f49e; 前言 hello hello~ &#xff0c;这里是大耳朵土土垚~&#x1f496;&#x1f496; &#xff0c;欢迎大家点赞&#x1f973;&#x1f973;关注&#x1f4a5;&#x1f4a5;收藏&#x1f339;&#x1f339;&#x1f339; &#x1f4a5;个人主页&#x…

博客网站SpringBoot+Vue项目练习

博客网站SpringBootVue简单案例 前言 学了vue后一直没用找到应用的机会&#xff0c;在Github上找到了一个看起来比较友好的项目&#xff08;其实具体代码我还没看过&#xff09;。而且这个项目作者的readme文档写的也算是比较好的了。 项目链接&#xff1a;https://github.c…

Docker入门指南:Docker容器的部署(一)

&#x1f340; 前言 博客地址&#xff1a; CSDN&#xff1a;https://blog.csdn.net/powerbiubiu &#x1f44b; 简介 当今软件开发领域中&#xff0c;Docker 成为了一种流行的容器化技术。Docker 可以帮助开发者将应用程序及其依赖项打包到一个独立且可移植的容器中&#xf…

AAA、RADIUS、TACACS、Diameter协议介绍

准备软考高级时碰到的一个概念&#xff0c;于是搜集网络资源整理得出此文。 概述 AAA是Authentication、Authorization、Accounting的缩写简称&#xff0c;即认证、授权、记帐。Cisco开发的一个提供网络安全的系统。AAA协议决定哪些用户能够访问服务&#xff0c;以及用户能够…

面试集中营—Spring篇

Spring 框架的好处 1、轻量&#xff1a;spring是轻量的&#xff0c;基本的版本大约2MB&#xff1b; 2、IOC&#xff1a;控制反转&#xff0c;Spring的IOC机制使得对象之间的依赖不再需要我们自己来控制了&#xff0c;而是由容易来控制&#xff0c;一个字&#xff1a;爽&#xf…