[Linux打怪升级之路]-信号的产生

前言

作者小蜗牛向前冲

名言我可以接受失败,但我不能接受放弃

  如果觉的博主的文章还不错的话,还请点赞,收藏,关注👀支持博主。如果发现有问题的地方欢迎❀大家在评论区指正

目录

一、信号基础知识

1、信号是什么

2、信号的定义

3、信号的处理方式 

二、有关信号操作的函数

1、signal函数(捕捉信号)

2、kill函数

3、raise函数

4、abort函数

三、信号的产生 

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

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

3、硬件异常产生信号 

4、由软件条件产生信号 


本期学习目标: 了解什么是信号,明白部分信号操作的相关函数,理解信号产生的过程

一、信号基础知识

1、信号是什么

        在日常生活中,我们在遇到十字路口过马路,我们知道红灯停绿灯行。为什么我们能够知道红灯停绿灯行呢?这不难理解,这些规则都刻画在我们的脑海中了,也就是说红灯其实就是一种信号,当我们识别红灯信号,大脑就会做出反应控制身体不动,当然有时候会人闯红灯,信号肯定是会传递了的,也就是说其实信号也是可以被忽略的。

那在Linux下的信号又是指的是什么

下面我们见一个现象

这里我们运行程序,当我们按下Ctrl+c将的时候,我们发现进程退出。

 这也就说明,操作系统肯定给进程发送了一个信号。

操作系统发送一个SIGINT信号给当前正在运行的进程。SIGINT信号表示一个中断请求,它的默认行为是终止进程。

当你按下Ctrl+C时,终端会捕获这个键盘事件,并转发一个SIGINT信号给当前在前台运行的进程。这个信号告诉进程有一个中断请求,通常意味着用户希望终止该进程。

2、信号的定义

信号是一个用来表示事件或消息的物理量。在操作系统中,信号是一种软件中断,用于通知进程发生了某个事件。这种事件可能是硬件异常、用户键入特殊的终端控制字符,或者其他进程发送给该进程的消息。每个信号都有一个唯一的正数描述和一个符号名。例如,SIGINT是用于表示中断的信号,其编号通常是2。信号可以用于进程间的简单通信,也可以用于处理异步事件

在操作系统下我们可以通过:

//查看系统定义的命令
kill -l

在Linux中总共62共信号 

  • 每个信号都有一个编号和一个宏定义名称,这些宏定义可以在signal.h中找到,例如其中有定 义 #define SIGINT 2
  • 编号34以上的是实时信号,这里只讨论编号34以下的信号,不讨论实时信号。这些信号各自在什么条件下 产生,默认的处理动作是什么,在signal(7)中都有详细说明: man 7 signal
//查看7号手册
man 7 signal

3、信号的处理方式 

在Linux中,信号处理的常见方式包括以下几种:

  1. 忽略信号:进程可以选择忽略某些信号,即当这些信号发生时,进程不会执行任何操作。这种方式可以用于屏蔽一些不必要的信号干扰。
  2. 默认行为:对于每个信号,系统都定义了一个默认行为。如果进程没有为某个信号设置自定义处理函数,那么信号发生时将会执行默认行为。默认行为可以是终止进程、忽略信号、停止进程等。
  3. 自定义处理函数:进程可以通过信号处理函数来捕获信号,并在信号发生时执行自定义的操作。这种方式常用于实现一些特定的行为,比如在接收到某个信号时进行日志记录、清理资源等。进程可以使用signal函数或者sigaction函数来设置信号处理函数。

二、有关信号操作的函数

1、signal函数(捕捉信号)

功能:用于捕捉信号并执行相应的处理操作

原型:  sighandler_t signal(int signum, sighandler_t handler);

参数 :

        signum:要捕捉的信号编号

        handler:指定一个函数指针,它指向的信号处理函数将在接收到指定的信号时被调用

返回值:signal函数返回之前为sig信号设置的处理函数的地址

typedef void (*sighandler_t)(int);

这里我们要特别  sighandler_t其实是一个函数指针。

signal函数的第二个参数handler是一个指向信号处理函数的指针,也就是sighandler_t类型的函数指针。这个参数用于指定当信号发生时应该执行的函数。

使用handler参数的方式如下:

  1. 自定义信号处理函数:首先,你需要定义一个符合sighandler_t类型的函数,即接受一个整数参数(信号编号)并返回void的函数。这个函数将用于处理信号。例如:

    void my_handler(int signal_num) {  // 处理信号的代码  
    }
  2. 设置信号处理函数:使用signal函数来设置信号的处理函数。将信号编号和自定义的处理函数作为参数传递给signal函数。例如,为了设置SIGINT信号(通常由Ctrl+C发送)的处理函数为my_handler

    signal(SIGINT, my_handler);

注意:

  • 处理函数通常会根据信号编号采取不同的操作。因此,在处理函数中,你可以使用传递的信号编号来判断是哪个信号被接收。
  • 如果你将handler设置为SIG_IGN,那么信号将被忽略。
  • 如果你将handler设置为SIG_DFL,那么将采取信号的默认行为。

举例用法:

#include<iostream>
#include<unistd.h>
#include<signal.h>using namespace std;void my_handler(int signal_num)
{cout<< "接收到的信号码: "<<signal_num << " "<<"my_pid: "<< getpid()<<endl;
}
//测试
int main()
{signal(SIGINT,my_handler);while(1){cout<< "我是一个进程,我正在运行" <<endl;sleep(1);}return 0;
}

这里我们发现,当我们在按Ctrl+ c,进程不再中止,而是去执行my_handler函数中的操作,singal也捕获了2号命令。(这里我们是用kill -9 pid中止进程的) 。

2、kill函数

功能:用于向指定进程发送信号

原型: int kill(pid_t pid, int sig)

参数 :

       pid:数指定要发送信号的进程ID

        sig:参数指定要发送的信号类型

返回值:当调用成功时,kill函数返回0;否则,返回-1并设置errno来表示错误原因

 kill函数可以发送多种类型的信号,例如:

  • SIGINT:中断进程。通常是通过用户按下Ctrl+C来产生的。
  • SIGTERM:终止进程。这是一个终止进程的请求,进程可以捕获该信号进行清理操作,然后退出。
  • SIGKILL:强制终止进程。这个信号会直接终止进程,进程无法捕获或忽略它。 

3、raise函数

功能:用于发送信号给当前进程

原型:int raise(int sig)

参数 :

        sig:参数指定要发送的信号类型

 当调用raise函数时,它会向当前进程发送指定的信号。如果信号处理程序已经注册了对应的信号,它将被调用进行处理。否则,默认的信号处理动作将被执行,可能会导致进程终止、停止或忽略信号。

举例:

#include<iostream>
#include<unistd.h>
#include<signal.h>
#include<sys/types.h>using namespace std;void my_handler(int signal_num)
{cout<< "接收到的信号码: "<<signal_num << " "<<"my_pid: "<< getpid()<<endl;
}
//测试
int main()
{//注册信号处理程序signal(SIGINT,my_handler);cout<<"SIGINT to slef"<<endl;//发送SIGINT给当前进程raise(SIGINT);cout<<" signal after" <<endl;return 0;
}

 

4、abort函数

功能:用于异常终止程序的函数

原型:void abort(void);

调用abort函数将导致程序异常终止,并返回一个非零的退出码给操作系统,以指示程序异常结束。

由于abort函数会立即终止程序,所以它通常会在发现严重错误的情况下使用,例如内存泄漏、无效参数等。它向程序员提供了一种机制,用于在无法恢复正常执行流程的情况下紧急停止程序

然而,由于abort函数不进行任何清理操作,因此不建议在正常情况下使用它来结束程序。在大多数情况下,应该尝试通过其他方式恢复程序的执行,或者使用更适当的函数来进行正常的程序终止,如exit函数。

三、信号的产生 

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

通过终端按键产生信号是指用户在终端(命令行界面)按下特定的按键组合,向当前进程发送一种信号。这种信号可以是一种通知或请求,告诉进程执行某种特定的操作

在Unix和类Unix系统中,终端按键可以产生信号,这些信号可以与进程进行交互。例如,当用户按下 Ctrl+C 组合键时,会向当前正在运行的进程发送一个 SIGINT 信号。这个信号的默认处理动作是终止进程

类似地,还有其他按键组合可以发送不同的信号,比如 Ctrl+\ 会发送 SIGQUIT 信号默认处理动作也是终止进程,并生成 core dump 文件

这种通过终端按键产生信号的方式,提供了一种用户与进程交互的手段,用户可以通过这种方式控制进程的行为,比如终止进程、暂停进程等。同时,进程也可以根据自身需要对这些信号进行处理,比如忽略信号、自定义处理函数等。

那么core dump (核心转储)又是指的什么呢?

       首先解释什么是Core Dump。当一个进程要异常终止时,可以选择把进程的用户空间内存数据全部 保存到磁盘上,文件名通常是core,这叫做Core Dump。

        进程异常终止通常是因为有Bug,比如非法内存访问导致段错误, 事后可以用调试器检查core文件以查清错误原因,这叫做Post-mortem Debug(事后调试)。一个进程允许产生多大的core文件取决于进程的Resource Limit(这个信息保存 在PCB中)。默认是不允许产生core文件的, 因为core文件中可能包含用户密码等敏感信息,不安全。在开发调试阶段可以用ulimit命令改变这个限制,允许 产生core文件。 首先用ulimit命令改变Shell进程的Resource Limit,允许core文件最大为1024K: $ ulimit -c 1024

这里本系统出于上面所说的安全等因素考虑core file size文件数量是0 

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

这里我们可以通过系统函数kill,raise,abort函数来向进程发送我们想要的信息

 int cnt = 0;while(cnt <= 10){printf("cnt : %d ,pid: %d\n",cnt++,getpid());sleep(1);// if(cnt >= 5) abort();if(cnt >= 5) raise(9);}

 调用abort终止程序

 调用raise(9)终止程序

3、硬件异常产生信号 

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

除以0的指令

当我们在编写的程序进行除0操作,为什么就会发送8好信号终止进程呢?

通过下图进行理解:

当我们进行除0操作,在CPU中有一个专门用来检测运算的,状态寄存器,里面有一个溢出标记位当除0时,标记位就会溢出,从而被操作系统知晓,发送8好信号给进程。 

非法内存地址 

OS怎么知道呢??我野指针了呢?,为什么程序中有野指针就会崩溃?

我们知道,其实指针指向的是虚拟地址,通过页表映射到物理地址,当指针越界访问的时候,操作系统就可以察觉到,从而对进程发送11号信号终止进程。

void my_handler(int signal_num)
{cout<< "接收到的信号码: "<<signal_num << " "<<"my_pid: "<< getpid()<<endl;sleep(1);
}
//测试
int main(int argc, char *argv[])
{//硬件异常产生信号signal(11,my_handler);while(true){std::cout << "我在运行中...." << std::endl;sleep(1);int *p = nullptr; //写一个野指针*p = 1;}return 0;
}

4、由软件条件产生信号 

在理解软件条件产生信号之前,我们需要理解allarm函数

头文件:#include unsigned

类型:int alarm(unsigned int seconds);

用途:调用alarm函数可以设定一个闹钟,也就是告诉内核在seconds秒之后给当前进程发SIGALRM信号, 该信号的默认处理动 作是终止当前进程

这个函数的返回值是0或者是以前设定的闹钟时间还余下的秒数。打个比方,某人要小睡一觉,设定闹钟为30分钟之后 响,20分钟后被人吵醒了,还想多睡一会儿,于是重新设定闹钟为15分钟之后响,“以前设定的闹钟时间还余下的时间”就 是10分钟。如果seconds值为0,表示取消以前设定的闹钟,函数的返回值仍然是以前设定的闹钟时间还余下的秒数

void my_handler(int signal_num)
{cout<< "接收到的信号码: "<<signal_num << " "<<"my_pid: "<< getpid()<<endl;exit(1);
}
//测试
int main(int argc, char *argv[])
{//4、软件异常产生信号int cnt = 0;signal(SIGALRM,my_handler);//统计1秒数据能够累计到多少alarm(1);while(true){cnt++;cout<< cnt <<endl;}return 0;
}

这里是没有用signal捕捉信号 

 这里是用singal捕捉了14号信号

 从现象上来看,当调用alarm函数,在经过设置的时间后,会给进程发送14号信号。

总结思考

上面所说的所有信号产生,最终都要有OS来进行执行,为什么?

这是因为操作系统是计算机硬件和软件之间的中介,负责管理计算机的资源,并提供一个运行环境,使得程序能够在计算机上正确、高效地运行。 

 OS是进程的管理者 信号的处理是否是立即处理的?

信号的处理并不一定是立即的,它取决于信号的类型以及接收进程的状态 

在合适的时候 信号如果不是被立即处理,那么信号是否需要暂时被进程记录下来? 

是的,如果一个信号在适当的时候不能立即处理,操作系统通常会将信号暂时记录下来,以便稍后处理。这种记录信号的机制通常称为信号队列(Signal Queue)。

信号队列允许操作系统将接收到的信号排队,而不会丢失它们。每个进程都有一个相关联的信号队列,用于存储接收到的信号。当信号到达时,它会被添加到接收进程的信号队列中。接收进程可以随后检查队列中的信号并决定如何处理它们。

一个进程在没有收到信号的时候,能否能知道,自己应该对合法信号作何处理呢? 

一个进程可以预先定义信号的处理方式,无论是否收到信号。这就是通过设置信号处理程序(Signal Handler)来实现的。信号处理程序是一段特定的代码,它规定了进程在接收特定信号时应采取的操作。

进程可以使用操作系统提供的系统调用(如signal()sigaction()等,具体取决于操作系统和编程语言)来注册信号处理程序。一旦设置了信号处理程序,当进程接收到相应的信号时,操作系统将执行信号处理程序中定义的操作。

如何理解OS向进程发送信号?能否描述一下完整的发送处理过程?

操作系统(OS)可以向进程发送信号,这是一种进程间通信的机制,用于通知接收进程发生了某些事件或条件。下面是完整的发送信号的处理过程:

  1. 信号产生:信号的产生通常是由操作系统、其他进程或硬件事件引发的。信号可以表示不同的事件,如用户按下终止键(Ctrl+C),进程除零错误,或者定时器超时等。

  2. 信号的种类:每个信号都有一个唯一的标识符,如SIGTERM、SIGINT、SIGSEGV等,用来表示不同的事件类型。操作系统和程序员可以根据需要定义自定义信号。

  3. 信号发送:操作系统将信号发送给目标进程。这通常涉及到操作系统查找目标进程的进程标识符(PID)或进程组标识符(PGID),然后将信号发送给目标。

  4. 信号传递:操作系统将信号传递给目标进程。目标进程的执行可能会被中断,以便它可以处理接收到的信号。这意味着操作系统在进程执行期间会修改进程的执行流程,以引发信号处理。

  5. 信号处理程序执行:目标进程在接收到信号后,会查找它为该信号注册的信号处理程序。信号处理程序是一段用户定义的代码,用于定义在接收信号时应执行的操作。处理程序可以是默认的操作,用户自定义的操作,或者忽略信号。

  6. 信号处理:根据信号处理程序的定义,进程采取相应的操作。这可以包括终止进程、忽略信号、记录事件、执行自定义操作等。处理程序执行后,进程可以继续执行原来的任务。

  7. 信号传递结果:在信号处理程序执行后,进程可以向操作系统报告信号的处理结果,通常以退出状态码的形式。这可以帮助操作系统了解信号处理的成功与否,以及进程是否需要进一步处理。

总结来说,操作系统向进程发送信号的过程涉及信号的产生、发送、传递、处理,以及可能的反馈。这种机制使得操作系统和不同进程之间能够进行通信和协作,通常用于处理异常情况、用户交互、进程控制等。

 

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

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

相关文章

Docker Stack部署应用详解+Tomcat项目部署详细实战

Docker Stack 部署应用 概述 单机模式下&#xff0c;可以使用 Docker Compose 来编排多个服务。Docker Swarm 只能实现对单个服务的简单部署。而Docker Stack 只需对已有的 docker-compose.yml 配置文件稍加改造就可以完成 Docker 集群环境下的多服务编排。 stack是一组共享…

专访HuggingFace CTO:开源崛起、创业故事和AI民主化丨智源独家

导读 HuggingFace CTO Julien Chaumond认为&#xff0c;在大模型时代&#xff0c;AI民主化至关重要。随着大语言模型和复杂人工智能系统的崛起&#xff0c;持续提升AI技术的可及性有助于确保这些技术的获取和控制不集中在少数强大实体手中。技术民主化促进了机会均等&#xff0…

「Java开发指南」如何用MyEclipse搭建Spring MVC应用程序?(一)

本教程将指导开发者如何生成一个可运行的Spring MVC客户应用程序&#xff0c;该应用程序实现域模型的CRUD应用程序模式。在本教程中&#xff0c;您将学习如何&#xff1a; 从数据库表的Scaffold到现有项目部署搭建的应用程序 使用Spring MVC搭建需要MyEclipse Spring或Bling授…

工程(十二)Ubuntu20.04LSD_SLAM运行

LSD_SLAM适配于ubuntu20.04修改过程的参考连接如下 Ubuntu20.04配置并运行LSD_SLAM&#xff0c;实测可行_nice-wyh的博客-CSDN博客 【已解决】/lib/x86_64-linux-gnu/libapr-1.so.0: undefined reference to uuid_generateUUID_1.0_Mr.Winter的博客-CSDN博客 博主将修改好的…

jar包的精细化运营,Java模块化简介 | 京东云技术团队

图&#xff1a;模块化手机概念 一、什么是Java模块化 Java模块化&#xff08;module&#xff09;是Java9及以后版本引入的新特性。 官方对模块的定义为&#xff1a;一个被命名的&#xff0c;代码和数据的自描述集合。&#xff08; the module, which is a named, self-descri…

css-inpu边框

效果图&#xff1a; input {width: 225px;height: 25px;background-color: #1469bd00;border: #aca9a97d solid 1px;color: white;font-size: 15pt;box-sizing: conte-box; }input:focus {border-style: solid;border-color: #03a9f4;box-shadow: 0 0 15px #03a9f4;outline: …

Flink往Starrocks写数据报错:too many filtered rows

Bug信息 Caused by: com.starrocks.data.load.stream.exception.StreamLoadFailException: {"TxnId": 2711690,"Label": "cd528707-8595-4a35-b2bc-39b21087d6ec","Status": "Fail","Message": "too many f…

3、Python基础语法:解释器、标识符、关键字、缩进

文章目录 Python解释器标识符关键字缩进代码示例与运行结果Python是一种高级编程语言,以其简洁明了的语法和强大的功能而受到广泛欢迎。本文将介绍Python的一些基础语法元素,包括解释器、标识符、关键字和缩进,并提供相应的代码示例和运行结果。 Python解释器 Python是一种…

HTML样式CSS、图像

HTML样式-CSS: CSS (Cascading Style Sheets) 用于渲染HTML元素标签的样式。CSS可以通过以下方式添加到HTML中&#xff1a;1&#xff09;、内联方式&#xff1a;在HTML元素中使用“style”属性&#xff1b;2&#xff09;、内部样式表&#xff1a;在HTML文档头部<head>区…

Java EE进阶2

包如果下载不下来怎么办? 1,确认包是否存在 2.如果包存在就多下载几次 3.如果下载了很多次都下载不下来,看看是不是下面几步出现了问题? 1)是否配置了国内源 settings.xml 2)目录是否为全英文,存在中文的话就修改路径 3)删除本地仓库的 jar 包,重新下载(可能由于网络的原…

二进制代码反汇编逆向工具:IDA Pro(WinMac)v7.7 汉化版

IDA Pro是一款交互式的、可编程的、可扩展的、多处理器的、交叉Windows或Linux WinCE MacOS平台主机来分析程序。它被公认为最好的花钱可以买到的逆向工程利器&#xff0c;已经成为事实上的分析敌意代码的标准并让其自身迅速成为攻击研究领域的重要工具。 IDA Pro的特点主要包括…

LangChain+LLM实战---ChatGPT的工作原理

一个词一个词的输出 ChatGPT能够自动生成类似于人类书写的文本&#xff0c;这是非常了不起和出乎意料的。但它是如何做到的&#xff1f;为什么会有效果呢&#xff1f;我的目的在于大致概述ChatGPT内部发生了什么&#xff0c;然后探讨它为什么能够很好地生成我们认为有意义的文…

干货满满,mac屏幕录制实用教程!

在当今科技飞速发展的时代&#xff0c;屏幕录制已经成为了人们日常生活中经常使用的功能&#xff0c;无论是工作还是生活&#xff0c;我们都需要使用到屏幕录制软件来捕捉屏幕上的内容。mac作为苹果公司开发的操作系统&#xff0c;拥有许多内置的屏幕录制工具。本文将详细介绍两…

【六、http】go的http的客户端重定向

一、http的重定向 重定向过程&#xff1a;客户浏览器发送http请求----》web服务器接受后发送302状态码响应及对应新的location给客户浏览器–》客户浏览器发现是302响应&#xff0c;则自动再发送一个新的http请求&#xff0c;请求url是新的location地址----》服务器根据此请求寻…

win10下.net framework 3.5 | net framework 4 无法安装解决方案

.net缺失解决方案 win10 .net framework 3.5组策略设置方案一方案二 win10 .net framework 4 参考文章 win10 .net framework 3.5 组策略设置 方案一 搜索组策略&#xff0c;依次展开“计算机配置”、“管理模板”&#xff0c;然后选择“系统”&#xff0c;找到指定可选组件…

Panda3d 相机控制

Panda3d 相机控制 文章目录 Panda3d 相机控制Panda3d中的透视镜头和垂直镜头透视镜头垂直镜头 Panda3d 中用代码控制相机的移动用键盘控制相机的移动用鼠标控制相机的移动 Panda3d 把相机也当做是一个 PandaNode&#xff0c;因此可以向操作其他节点对其进行操作。 真正的相机是…

使用pandas处理excel文件【Demo】

一、代码示例 import pandas as pd from pandas import Series,DataFrame from pandasql import sqldf import matplotlib.pyplotidInfos DataFrame(pd.read_excel(home_data.xlsx))print(idInfos.head(2))print(idInfos.dtypes)# print(idInfos[:][姓名]) # 自定义一个函数s…

音视频报警可视对讲15.6寸管理机

音视频报警可视对讲15.6寸管理机 一、管理机技术指标&#xff1a; 1、15.6寸原装京东方工业液晶触摸屏&#xff0c;分辨率1920 (H) x 1080 (V)&#xff1b; 2、1000M/100M自适应双网口&#xff1b; 4、按键设置&#xff1a;报警/呼叫按键&#xff0c;通话/挂机按键&#xff…

AGV 导航方法总体设计与分析

导航方案设计 导航方法分为三个部分——路径规划、实时定位、轨迹纠正。 视觉定位模块 主要有三个工作任务&#xff1a;利用二维码定位对 AGV 初始位姿 进行矫正&#xff0c;保证 AGV 初始运动时其运动轨迹能够与道路平行&#xff1b;利用 ORBSLAM 定位能够实时的获得 AGV 在…

WebSocket Day 01:入门案例

前言 欢迎来到WebSocket入门案例系列的第一天&#xff01;在今天的博客中&#xff0c;我们将一起探索WebSocket的基础知识和使用方法。本系列将以一个简单的入门案例为基础&#xff0c;带领您逐步了解WebSocket的原理和用法。 一、什么是 WebSocket ? WebSocket是一种在Web应…