Linux进程信号处理:深入理解与应用(1)

 

                                               🎬慕斯主页修仙—别有洞天

                                              ♈️今日夜电波:it's 6pm but I miss u already.—bbbluelee

                                                                0:01━━━━━━️💟──────── 3:18
                                                                    🔄   ◀️   ⏸   ▶️    ☰  

                                      💗关注👍点赞🙌收藏您的每一次鼓励都是对我莫大的支持😍


 

目录

Linux进程信号的概念

引入进程信号

信号的产生

回顾进程的运行

认识进程信号

通过signal替换信号

通过raise给自己发信号

通过abort终止自己

具体信号的产生(下一篇内容)


Linux进程信号的概念

        什么叫做信号?Linux进程信号是一个通知机制,用于在进程之间传递信息和控制进程行为。

        Linux进程信号是操作系统中的一种通信机制,它允许内核或其他进程向目标进程发送消息,这些消息被称为信号。信号可以由多种事件触发,包括硬件异常、软件事件、用户交互等。例如,当用户按下Ctrl+C时,会产生一个中断信号(SIGINT),通知前台进程终止。

        此外,Linux系统中的信号可以分为两类:非可靠信号和可靠信号。非可靠信号是Unix早期版本的信号,编号为1到31。它们不可靠,因为不能被捕获或忽略。可靠信号编号为34到64,它们可以被捕获、阻塞或忽略,提供了更多的控制能力

        综上所述,Linux进程信号是一种灵活的通信机制,它不仅能够响应系统事件,还能够实现进程间的通信和控制。了解信号的概念对于进行Linux编程和管理是非常重要的。

引入进程信号

        通过一个例子了解信号:日常生活中,我们经常会碰到红绿灯,当我们看到红绿灯时就算是碰到了一种信号,我们在看到它后会识别它,“即便没看到红绿灯我们也知道对应的灯亮,意味着什么”,当你等红绿灯的时候可能在玩手机或者干什么,但是“红绿灯信号的变化跟你看手机这件事是不冲突的”,此时,在看手机的你注意到变成了红绿灯变成了绿灯,但是,“你没有立刻过红绿灯而是用手机回完信息后才走”。

         上面提到的种种行为实际上也对应着进程信号的种种特点:(1)信号没有产生的时候,其实我们就已经知道怎么处理这信号了。(2)信号的到来,我们并不清楚具体是什么时候,信号的到来期间,我正在做工作,所以信号的产生是异步的。(3)信号产生了,我们不一定立刻去处理它,而是我们在合适的时候处理。

信号的产生

回顾进程的运行

        进程在运行时,前台进程(命令行操作的时候)只能有一个,后台进程可以有多个。一般情况下,可以通过 Ctrl+c 终止前台进程,Ctrl+z 暂停进程。我们通过进程有没有能力接受用户输入来判断是否为前台进程。

        需要注意的是:前台进程不能被暂停(也就是Ctrl+z),被Ctrl+z后会被放到后台再暂停。如果被shell也是进程,OS会自动把shell提到前台或者后台。

        通过以下命令来设置对应的前、后台进程:

./exe程序 //运行前台进程
./exe程序 & //运行后台进程

        通过以下命令来查看后台进程:

jobs //查看所有后台进程的属性

        通过以下命令来改变后台进程:

bg number //让进程在后台跑起来
Ctrl+z 接着 bg number //将进程放到后台运行
fg number //把进程放到前台运行

        通过以下命令查看运行的进程:

ps axj //后面通常还会跟管道过滤

认识进程信号

        通过kill -l 命令查看所有进程信号:

        通常我们将1-31号的信号称为普通信号,34-64称为实时信号,没有0号、32号、33号信号!

        需要注意的是:每一个进程都有一张自己的函数指针数组,数组的下标和信号编号强相关。实际上,进程可以通过类似位图的数据结构表示自己是否收到了信号:比特位的位置,决定信号编号;内容,决定是否收到信号。可以简要理解:(后续介绍signal()函数会理解位图以及函数指针数组)

struct task_struct
{//信号位图0000 0010uint32_t sigmap;//...
}

        无论信号有多少种产生方式,永远只能让OS向目标进程发送!为什么呢?因为OS是所有进程的管理者!

通过signal替换信号

        signal()函数的原型如下:

typedef void (*sighandler_t)(int);sighandler_t signal(int signum, sighandler_t handler);
  • 参数
    • signum:这是信号的编号,代表你想要处理的信号类型。可以通过终端输入kill -l来查看系统支持的信号列表。需要注意的是,并非所有信号都可以被捕获,例如SIGKILL和SIGSTOP信号是不能被捕获的。
    • handler:这是一个函数指针,指向当信号发生时应该调用的函数。如果不希望改变信号的默认行为,可以将此参数设置为特定的常数。
  • 处理方式
    • SIG_IGN:表示忽略该信号,即信号发生时不采取任何行动。
    • SIG_DFL:表示采用系统默认的处理方式,通常是终止进程或忽略该信号。
    • 自定义处理函数:如果你希望在信号发生时执行特定的操作,可以设置一个自定义的处理函数。这个函数通常需要接受一个整数参数(信号编号)并且返回void。

        signal()函数的作用是为指定的信号设置处理程序。当进程接收到指定的信号时,会调用相应的处理程序。如果之前已经为该信号设置了处理程序,signal()函数会返回之前设置的处理程序指针;如果没有设置过,将返回SIG_DFL,表示使用默认的信号处理方式。需要注意的是:9号信号不能自定义处理函数。(因为定义了也不起作用)

        如下为一个替换的例子:

#include <iostream>
#include <unistd.h>
#include <signal.h>void handler(int signo)
{std::cout<<"获得一个:NO."<<signo<<"信号"<<std::endl;
}int main()
{signal(2,handler);while(true){std::cout << "i am running~,pid:" <<getpid()<< std::endl;sleep(1);}return 0;
}

        如下,我们通过改变2号信号要调用的函数,从而改变了原来2号信号终止进程的功能:

        接着再看一个对比例子,左边为替换了2号信号调用的函数的进程,右边为正常的进程。可以注意到,当我们两个进程同时跑起来的时候,左边是不能用Ctrl+c终止的,右边确可以,这说明了修改信号只针对于某个进程,也就是说:我们的进程中每一个进程都有一张自己的函数指针数组,数组的下标和信号编号强相关,其中我们可以通过位图来映射这样的数组。

通过raise给自己发信号

   raise函数用于向当前进程发送指定的信号。其函数原型为:

#include<signal.h>
int raise(int sig);

        该函数接受一个参数sig,它是要发送的信号的编号。如果函数执行成功,返回值为0;如果失败,则返回非0值。以下是关于raise函数的一些详细解释:

  1. 功能raise函数的主要作用是允许一个进程给自己发送一个信号。这在某些情况下是有用的,比如当进程需要通知自己发生了某个事件或需要改变自身的行为时。
  2. 等价关系raise(sig)的功能等同于kill(getpid(), sig)。换句话说,raise函数相当于是kill函数的一个特殊形式,其中信号发送者(发送进程)和信号接收者(目标进程)是同一个进程。
  3. 错误处理:如果raise函数不能发送信号(例如,由于权限问题或无效的信号编号),它将返回一个非零错误代码。这与kill函数的错误处理行为类似。
  4. 信号处理:一旦raise函数成功发送了信号,接收进程(即发送进程本身)必须对该信号进行处理。信号的处理方式取决于进程预先设置的处理例程,或者如果没有设置,那么将采用默认的信号处理动作。
  5. 使用场景raise函数通常在需要自我通知或自我中断的场景中使用,比如在多线程程序中,一个线程可能需要通知另一个线程发生了某个事件而不需要其他进程介入。

        如下示例:

#include <iostream>
#include <unistd.h>
#include<signal.h>void handler(int signo)
{std::cout<<"获得一个:NO."<<signo<<"信号"<<std::endl;
}int main()
{signal(2,handler);while(true){std::cout << "i am running~,pid:" <<getpid()<< std::endl;sleep(1);raise(2);}return 0;
}

通过abort终止自己

        在Linux系统中,abort函数用于异常终止一个进程。具体来说,abort函数的作用如下:

        函数原型

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

        当调用该函数时,它会向当前进程发送SIGABRT信号,导致进程异常终止。

  1. 信号处理SIGABRT信号是一个由系统内核发出的信号,它通常用于表示程序出错或需要立即终止的情况。abort函数的实现通常是先取消对SIGABRT信号的阻塞,然后使用raise函数(或等效的机制)向当前进程发送该信号。
  2. POSIX规范:根据POSIX标准,调用abort函数后,进程必须被杀死。这意味着除非在信号处理例程中捕获并处理了SIGABRT信号,否则进程将不可避免地被终止。
  3. 程序示例:在实际编程中,当检测到严重错误或无法恢复的情况时,可以使用abort函数来立即停止程序运行。例如,如果内存分配失败或者程序配置错误,调用abort可以快速结束程序,避免进一步的错误操作。

        综上所述,abort函数是Linux中用于异常终止进程的一个重要函数,它通过发送SIGABRT信号来实现这一目的。在应用程序开发中,合理使用abort函数可以帮助开发者处理不可预见的错误情况,确保程序的稳定性和安全性。

        如下示例:

#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>void handler(int signo)
{std::cout<<"获得一个:NO."<<signo<<"信号"<<std::endl;
}int main()
{signal(SIGABRT,handler);abort();while(true){std::cout << "i am running~,pid:" <<getpid()<< std::endl;sleep(1);}return 0;
}

具体信号的产生(下一篇内容)

通过终端按键产生信号
调用系统函数向进程发信号
由软件条件产生信号
硬件异常产生信号

 


                   感谢你耐心的看到这里ღ( ´・ᴗ・` )比心,如有哪里有错误请踢一脚作者o(╥﹏╥)o! 

                                       

                                                                        给个三连再走嘛~  

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

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

相关文章

关于破解IDEA后启动闪退的问题

问题描述&#xff1a;2023.1启动不了&#xff0c;双击桌面图标&#xff0c;没有响应。 解决办法&#xff1a; 打开C:\Users\c\AppData\Roaming\JetBrains\IntelliJIdea2023.1\idea64.exe.vmoptions 这个文件。 内容如下所示&#xff1a; 删除红框的数据以后&#xff0c;再登录…

基于springboot校园二手书交易管理系统源码和论文

在Internet高速发展的今天&#xff0c;我们生活的各个领域都涉及到计算机的应用&#xff0c;其中包括乐校园二手书交易管理系统的网络应用&#xff0c;在外国二手书交易管理系统已经是很普遍的方式&#xff0c;不过国内的管理系统可能还处于起步阶段。乐校园二手书交易管理系统…

【Spring连载】使用Spring Data访问Redis(十四)----Redis Repositories

【Spring连载】使用Spring Data访问Redis&#xff08;十四&#xff09;----Redis Repositories 一、Core concepts二、Defining Repository Interfaces三、Creating Repository Instances四、Usage五、Object Mapping Fundamentals六、对象到Hash映射Object-to-Hash Mapping七、…

JC/T 2569-2020 建筑门窗用木型材检测

木型材指使用天然木材或以天然木材为基材加工成的指接材、集成材、重组木等为原料&#xff0c;沿纤维方向加工出一定尺寸槽口的们穿框扇杆件。 JC/T 2569-2020建筑门窗用木型材检测: 检测项目 测试方法 外观 JC/T 2569 尺寸 JC/T 2569 剪切强度 GB/T 26899 剥离率 GB…

搜狗开源框架Workflow网络模型分析

workflow是一个比较轻量化的后端服务框架&#xff0c;支持Linux/Mac/Windows主流平台&#xff0c;其网络模块是框架的核心。在workflow-windows分支上可以看到对windows的IOCP的封装&#xff0c;对于学习windows IOCP网络编程有很好的启发意义。因此&#xff0c;有必要对该网络…

2024-02-01(Hive)

1.我们通过忘Hive中执行SQL语句&#xff0c;Hive会帮我们将SQL语句翻译成MapReduce在底层去做分布式的计算。 2.Hive看似处理的是mysql的表&#xff0c;但实际上处理的是HDFS中的文本文件。 3.Hive中创建的库和表的数据&#xff0c;存储在HDFS中&#xff0c;默认存放在&#…

类银河恶魔城学习记录1-1 Player状态机的搭建 P28上

对状态机的介绍 什么是状态机&#xff1f;一篇文章就够了 - 知乎 说实话&#xff0c;目前并不能深入理解状态机的奇妙之处&#xff08;当然&#xff0c;我觉得状态机作为教程的重要组成部分是不得不理解的&#xff0c;所以以下我会对游戏教程内的状态机做一些我认为的解释&am…

加速知识检索:伯克利DeepMind联合研究,RaLMSpec让语言模型服务飞速提升2-7倍!

近年来&#xff0c;随着大型语言模型&#xff08;LLM&#xff09;的出现&#xff0c;在多样化的 NLP 任务上取得了令人瞩目的成果。然而&#xff0c;知识密集型任务仍是 NLP 领域中的一项挑战&#xff0c;因为这些任务不仅要求模型要理解和生成自然语言&#xff0c;还要能够访问…

Tomcat在Java web的应用

Tomcat在Java web的应用 本来这篇博客顺应之前的内容&#xff0c;应该是需要写Tomcat的简介、基本使用、配置和部署项目、Web的项目结构、创建MavenWeb、idea本地集成以及Tomcat的Maven插件的笔记内容&#xff0c;但是总觉得没必要&#xff0c;因为这些内容网上肯定很多了&…

移动机器人激光SLAM导航(四):GMapping SLAM 篇

参考引用 GMapping ROS-Wiki从零开始搭二维激光SLAMGMapping 漫谈小白学移动机器人机器人工匠阿杰wpr_simulation 移动机器人激光SLAM导航&#xff08;文章链接汇总&#xff09; 1. GMapping 1.1 FastSLAM 问题分解 概率论相关公式 1. 条件概率公式&#xff1a; p ( x , y ) …

C语言的基础学习

C语言的变量 ## C语言中的变量 在C语言中,变量是对程序中数据所占内存空间的一种抽象定义。定义变量时,用户定义变量的名、变量的类型,这些都是变量的操作属性。不仅可以通过变量名访问该变量,系统还通过该标识符确定变量在内存中的位置 [❷](https://www.dotcpp.com/cour…

将链表反转

反转链表在解决需要从尾节点开始遍历到头节点的地方很实用&#xff0c;是一种常用的解题技巧。在反转时&#xff0c;我们可以考虑从前向后反转和从后向前反转两种方式。 法一&#xff1a;递归 每次将链表的头节点的下一个节点作为新的头节点&#xff0c;然后对剩余部分调用递归…

深度学习入门笔记(五)前馈网络与反向传播

接着上一节,本节讲解模型自我学习的数学计算过程究竟是怎么样的。 5.1 前馈网络 一个最简单的前馈神经网络如图所示,对于每一个隐藏层,输入对应前一层每一个节点权重乘以节点输出值,输出则是经过激活函数(例如sigmoid函数)计算后的值。 在这样的网络中,输入的数据 x 经…

HSMES中的计划管理

MES&#xff08;生产制造执行系统&#xff09;&#xff0c;旨在帮助制造企业实现高质量和高效率生产&#xff0c;通过实时监控生产过程&#xff0c;智能的生产计划排程&#xff0c;灵活的工艺设置&#xff0c;进而实现企业的数字化生产。对于生产计划的管控是其中不可或缺的一环…

jQuery 获取并设置 CSS 类 —— W3school 详解 简单易懂(十五)

通过 jQuery&#xff0c;可以很容易地对 CSS 元素进行操作。 jQuery 操作 CSS jQuery 拥有若干进行 CSS 操作的方法。我们将学习下面这些&#xff1a; addClass() - 向被选元素添加一个或多个类removeClass() - 从被选元素删除一个或多个类toggleClass() - 对被选元素进行添…

Python 抽象类

在Python中,抽象类是一种特殊的类,不能直接实例化,而是被用作其他类的基类。它定义了一组方法的接口,但没有具体的实现。子类必须实现这些方法才能实例化。 要创建一个抽象类,您需要使用abc模块中的ABC(Abstract Base Class)类,并通过将metaclass设置为ABCMeta来指定它…

小白水平理解面试经典题目_数组类Leetcode 412. Fizz Buzz【数学解法】

412 FizzBuzz 小白渣翻译&#xff1a; 给定一个整数 n &#xff0c;返回一个字符串数组 answer &#xff08;从 1 开始索引&#xff09;&#xff0c;其中&#xff1a; answer[i] “FizzBuzz” 如果 i 能被 3 和 5 整除。answer[i] “Fizz” 如果 i 能被 3 整除。answer[i]…

8. 实现VLAN间的通信

文章目录 一. 初识VLAN通信1.1. VLAN的概述1.2. Dot1q 终结子接口(单臂路由 )1.3. VLANIF接口 二. 实验专题2.1. 实验1&#xff1a; Dotlq 终结子接口2.1.1. 实验目的2.1.2. 实验拓扑图2.1.3. 实验步骤&#xff08;1&#xff09;配置PC机的网络&#xff08;2&#xff09;交换机…

八、访存顺序(Memory Ordering)

前言 这部分的内容比较抽象&#xff0c;很多内容我无法理解&#xff0c;都是直接翻译过来的。虽然难&#xff0c;但是不可不看&#xff0c;如果遇到无法理解的都直接跳过&#xff0c;那后面都无法学习下去了。觉得无法理解是因为目前的知识还很欠缺&#xff0c;到后面具备了这…

React16源码: React中event事件触发的源码实现

event 事件触发过程 1 &#xff09;概述 在之前事件绑定时&#xff0c;绑定的是两个方法 一个是 dispatchInteractiveEvent另外一个 dispatchEvent 其实它们调用的方法都是差不多的&#xff0c;一开始会有一点小的区别 2 &#xff09;源码 定位到 packages/react-dom/src/ev…