[ Linux ] Linux信号概述 信号的产生

news2024/12/27 12:53:13

目录

0.问题引入:

0.1 将进程设置为后台进程

0.2 查看后台进程并将后台进程提至前台

0.3 将前台进程设置为后台进程

1.信号的概念

2.查看信号列表

3.信号处理的常见方式

4.信号的产生

4.1 用户层产生信号的方式

4.1.1通过终端按键产生信号

4.1.2调用系统函数向进程发信号(kill,raise,abort)

4.1.3由软件条件产生信号

4.1.4由硬件异常产生信号


0.问题引入:

在曾经我们学习Linux的经历中,我们也是多次使用信号的。比如:当我们在使用xshell时,在命令行中按Ctrl+c,这个键盘输入产生了一个硬件中断,被操作系统获取,解释成信号,发送给目标前台进程。前台进程因为收到了信号,进而引起进程退出。

注意:

  1. Ctrl+C 产生的信号只能发给前台进程。一个命令后面加个&可以放到后台运行,这样Shell不必等待进程结束就可以接受新的命令,启动新的进程。
  2. Shell可以同时运行一个前台进程和任意多个后台进程,只有前台进程才能接到像Ctrl+C这样控制键产生的信号。
  3. 前台进程在运行过程中用户随时可能按下Ctrl+C而产生一个信号,也就是说该进程的用户空间代码执行到任何地方都有可能收到SIGINT信号而终止,所以信号相对于进程的控制流程来说是异步(Asynchronous)的。

0.1 将进程设置为后台进程

./进程名 &

0.2 查看后台进程并将后台进程提至前台

我们发现进程一旦被设置成为后台进程是无法杀掉的,此时在一直死循环的打印hello myproc.那么我们怎么杀掉这个进程呢? 有一种方法是将后台进程设置成为前台进程,再使用ctrl +c 关闭。这种方法也比较好理解。因此哦我们在此处将使用这种方法关闭后台进程。

首先我们要查看此时都有什么任务在执行,我们可以输入 jobs 来查看当前任务列表

其次我们将这个进程提至提至前台,使用 fg 1 ,其中1是任务编号,也就是 [ ]+ 中的数字,注意fg 和 1之间需要带空格。

0.3 将前台进程设置为后台进程

我们刚刚知道了将后台进程提至前台使用 fg 任务号,f是front的意思。因此我们如果想把前台进程设置为后台进程,可以使用bg 任务号,我们可以来测试一下

1.信号的概念

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

2.查看信号列表

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

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

3.信号处理的常见方式

由于信号产生时是异步的,当产生信号的时候,对应的进程可能正在做着更重要的事情,因此这个进程可以暂时不处理这个信号!进程正在做着更重要的事情说明进程可能不需要理解处理这个信号!但是不代表这个信号不会被处理。进程是一定要记住这个信号已经来了(信号有吗?什么信号?)。因此信号处理的常见方式有以下三种:

  1. 忽略此信号
  2. 执行该信号的默认处理动作
  3. 提供一个信号处理函数,要求内核在处理该信号时切换到用户态执行这个处理函数,这种方式成为捕捉信号(Catch)。

4.信号的产生

我们在前面提到了我们现在只谈论1-31号信号。那么进程要处理一个信号,肯定是先描述,再组织。那么进程是如何记住这个信号的呢?当然是保存在进程的PCB中。由于我们只考虑1-31号进程,因此在进程的tast_struct中有一个uint32_t sig来表示信号。这里使用了位图的思想。什么信号产生我们使用的是比特位的位置。那么怎么判断有没有比特位的产生我们通过比特位的内容,1表示产生,0表示没有产生。因此一个uint_32足以表示1-31个信号了。

而tast_struct是内核的数据结构,因此只有操作系统有权利获得进程的所有属性。所以进程的整个生命周期,无论信号怎么产生,只能是操作系统帮我们进行信号的设置。

4.1 用户层产生信号的方式

4.1.1通过终端按键产生信号

产生信号的第一种方式是通过终端按键产生信号,也就是键盘。系统也为我们提供了一个signal函数,可以捕捉我们产生的信号,接下来我们将使用signal这个函数验证以下终端按键是可以产生信号的。

SIGINT的默认处理动作是终止进程,SIGQUIT的默认处理动作是终止进程并且Core Dump.现在我们可以来验证以下。

我们可以使用函数产生信号。我们可以使用signal函数向进程发送信号。

其中handler是函数指针(回调方法),是可以让我们用户自定义处理信号的接口(处理信号的第三种方式)

我们在C++代码中看看如何使用signal函数,在下面这段代码中,我们设置了如果进程接受到了SIGINT信号,就会调用handler函数从而输出指定内容。我们都是SIGINT是2号信号,键盘上按Ctrl +C 本质就是给前台进程发送2号信号,因此当进程跑起来的时候,我们按Ctrl + C时,程序会调用hanlder方法打印指定内容,我们来看看结果吧。

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

using namespace std;

void handler(int signo)
{
    cout<<"我是一个进程,刚刚获取了一个信号,信号编号是: "<<signo <<endl;
}

int main()
{
    signal(SIGINT,handler);

    sleep(3);
    cout<<"进程已经设置完了"<<endl;
    sleep(3);
    while(true)
    {
        cout<<"我是一个运行中的进程,我的pid: "<<getpid()<<endl;
        sleep(2);
    }
    
    return 0;
}

注意:这里当SIGINT产生的时候,才会调用hanlder方法。

在2号信号被我们自定义设置的时候,进程按2号信号是不会执行默认动作的,因此此时我们向终止进程不能使用ctrl + c了,我们可以按ctrl + \ , ctrl + \ 是给进程产生3号信号,我们也可以自定义3好信号。按这样的道理,我们可以将31个信号都自定义,那么这个进程是不是就可以不被kill 掉呢? 其实不是的!其中9号进程是不能被自定义设置的。因为操作系统要保护自己所以肯定不能将所有的信号都自定义。

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

using namespace std;

void handler(int signo)
{
    cout<<"我是一个进程,刚刚获取了一个信号,信号编号是: "<<signo <<endl;
}

int main()
{
    for(int sig=1;sig<=31;sig++)
    {
        signal(sig,handler);
    }

    //signal(SIGINT,handler);

    sleep(3);
    cout<<"进程已经设置完了"<<endl;
    sleep(3);
    while(true)
    {
        cout<<"我是一个运行中的进程,我的pid: "<<getpid()<<endl;
        sleep(2);
    }

    return 0;
}

那么我们知道了键盘可以产生信号,那么是谁给进程发送的信号呢? 答案当然是OS操作系统

4.1.2调用系统函数向进程发信号(kill,raise,abort)

使用kill函数发送信号

我们知道kill命令可以给一个进程发信号,例如我们之前经常使用kill -9 pid 来杀死进程编号为pid的进程。同时kill也是一个系统函数。我们也可以通过软件的方式,调用kill函数从而给指定进程发信号。

我们使用一段C++代码来演示一下kill函数是如何使用的:我们的计划是通过调用kill函数给指定进程发信号,模拟实现kill命令。因此当程序跑完是,如果输入./mykill 9 pid时候可以杀掉指定pid进程。

#include <iostream>
#include <cstring>
#include <errno.h>
#include <signal.h>
#include <sys/types.h>

#include <unistd.h>

using namespace std;

void handler(int signo)
{
    cout<<"我是一个进程,刚刚获取了一个信号,信号编号是: "<<signo <<endl;
}


static void Usage(const std::string &proc)
{
    cerr<<"Usage:\n\t" << proc << "signo pid"<<endl;
}

//我想写一个kill命令
// ./mykill 9 pid
int main(int argc,char* argv[])
{
    if(argc != 3) 
    {
        Usage(argv[0]);
        exit(1);
    }
 
    if(kill(static_cast<pid_t>(atoi(argv[2])),atoi(argv[1])) == -1)
    {
        cerr<<"kill :" <<strerror(errno) << endl;
    }
    return 0;
}

使用raise函数

raise是一个自取函数,是进程自己给自己发送信号。因此我们在代码中使用一下,加入我们要给自己发送2号信号。

#include <iostream>
#include <cstring>
#include <errno.h>
#include <signal.h>
#include <sys/types.h>

#include <unistd.h>

using namespace std;

void handler(int signo)
{
    cout<<"我是一个进程,刚刚获取了一个信号,信号编号是: "<<signo <<endl;
}


static void Usage(const std::string &proc)
{
    cerr<<"Usage:\n\t" << proc << "signo pid"<<endl;
}

//我想写一个kill命令
// ./mykill 9 pid
int main(int argc,char* argv[])
{
    signal(2,handler);//没有调用对应的handler方法,仅仅是注册


    while(true)
    {
        sleep(1);
        raise(2);//自己给自己发送2号信号
    }
    return 0;
}

使用abort函数

abort是想自己发送SIGABRT信号 -- 6号信号 我们也在代码中调用使用abort函数,并且捕捉以下该信号。

#include <iostream>
#include <cstdlib>
#include <cstring>
#include <errno.h>
#include <signal.h>
#include <sys/types.h>

#include <unistd.h>

using namespace std;

void handler(int signo)
{
    cout<<"我是一个进程,刚刚获取了一个信号,信号编号是: "<<signo <<endl;
}


static void Usage(const std::string &proc)
{
    cerr<<"Usage:\n\t" << proc << "signo pid"<<endl;
}

//我想写一个kill命令
// ./mykill 9 pid
int main(int argc,char* argv[])
{
    signal(6,handler);//没有调用对应的handler方法,仅仅是注册


    while(true)
    {
        sleep(1);
        abort();
        //raise(2);//自己给自己发送2号信号
    }
    return 0;
}

通过结果发现:我们刚刚确实捕捉到了6号信号,但是进程依然退出了。因此除了9号信号可以让进程退出。6号信号也可以。

4.1.3由软件条件产生信号

alarm

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

#include <iostream>
#include <cstdlib>
#include <cstring>
#include <errno.h>
#include <signal.h>
#include <sys/types.h>

#include <unistd.h>

using namespace std;
int main(int argc,char* argv[])
{
    alarm(3);//3秒后alarm
    int cnt = 0;
    while(true)
    {
        cout<< "我是一个进程 cnt : "<<cnt++<<endl;
        sleep(1);
    }
    return 0;
}

我们调用了alarm函数,经过3秒后闹钟会响,我们没有改变alarm的动作,因此当闹钟响起时,alarm函数会执行默认的终止进程的操作,因此经过3秒后,进程会自动退出。

这里有张图片 但是出不来  我把结果用代码的形式写到这里

[Lxy@VM-20-12-centos 11-28]$ ./mykill 
我是一个进程 cnt : 0
我是一个进程 cnt : 1
我是一个进程 cnt : 2
Alarm clock

4.1.4由硬件异常产生信号

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

当我们日常在写代码的时候,如果写了一个/0错误,进程会怎么样呢?

    int a = 10;
    cout<< a/0<<endl;

当我们运行时,直接会告诉我们浮点数异常!因此我们之前所写的C/C++代码,我们说程序崩溃了,现在我们站在系统的角度上面我们可以知道,所谓的程序崩溃说法不是很准确,其实是进程崩溃,进程崩溃的本质是该进程收到了异常信号! 我们写两个进程崩溃的例子。

#include <iostream>
#include <cstdlib>
#include <cstring>
#include <errno.h>
#include <signal.h>
#include <sys/types.h>

#include <unistd.h>

using namespace std;
int cnt = 0;
void handler(int signo)
{
    cout<<"我是一个进程,刚刚获取了一个信号,信号编号是: "<<signo << endl;
}


static void Usage(const std::string &proc)
{
    cerr<<"Usage:\n\t" << proc << "signo pid"<<endl;
}

//我想写一个kill命令
// ./mykill 9 pid
int main(int argc,char* argv[])
{
    for(int sig = 1;sig<=31;sig++) signal(sig,handler);
    int a = 10;
    cout<< a/0<<endl;
    return 0;
}

我们再来写一个数组的越界访问,看看效果

    int a[100];
    a[10000000] = 100;

(本篇完)

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

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

相关文章

【数据集研究】PASCAL VOC 2007

目录1、数据集地址2、适用的比赛1&#xff09;Main Competitions2&#xff09;Taster Competitions3、类别及类别的定义1&#xff09;数据集包含的类别2&#xff09;类别的定义4、数据集1&#xff09;训练集、验证集、测试集2&#xff09;图片和待检测物在类别的分布详情5、标注…

Kamiya丨Kamiya艾美捷小鼠BDNF ELISA原理分析

Kamiya艾美捷小鼠BDNF ELISA预期用途&#xff1a; 小鼠BDNF ELISA用于定量测定小鼠细胞培养物上清液、细胞裂解物、细胞培养物中的BDNF&#xff0c; 血清和血浆&#xff08;肝素、EDTA、柠檬酸盐&#xff09;。仅供研究使用。 引言&#xff1a; 脑源性神经营养因子&#xff…

多线程与高并发(13)——Java常见并发容器总结

本文总结常见的并发容器&#xff0c;包含ConcurrentHashMap、CopyOnWriteArrayList 、ConcurrentLinkedQueue、BlockingQueue 、ConcurrentSkipListMap&#xff0c;本文仅做简单的总结&#xff0c;不做详细的源码分析。 一、ConcurrentHashMap HashMap不是线程安全的&#xf…

Linux基本命令(三)——服务器搭建

搭建简单Web服务器 安装web服务 yum -y install httpd 启动httpd服务 systemctl start httpd查看httpd是否开启成功 service httpd status以下是状态信息&#xff1a; 重新启动httpd systemctl restart httpd6.进入主配置文件 vim /etc/httpd/conf/httpd.conf编辑自配置文件 v…

FPGA控制W5500完成UDP环回测试

FPGA控制W5500完成UDP环回测试&#xff11; 前言&#xff12; 前期准备&#xff13; &#xff37;5500寄存器描述4 &#xff37;5500 环回测试4.1 W5500初始化4.1.1 通用寄存器初始化4.1.2 socket寄存器初始化4.2 W5500数据接收4.3 W5500数据发送4.4 数据环回5 总结&#x…

RKMEDIA--VO的使用

这一节主要说说rkmedia vo模块的使用。 rkmedia的vo是对DRM接口的封装&#xff0c;提供给用户更方便的使用&#xff0c;rv1126/rv1109支持两个vo图层。 1、首先先介绍一下DRM的测试命令--modetest&#xff0c;用来确认当前屏幕能够正常点亮。 modetest -M rockchip //打印出…

融云艾瑞发布《政企数智办公平台行业研究报告》,解读数智化时代的办公新趋势

关注公众号文章扫码报名融云&艾瑞“政企数智办公报告及新品发布会” 近期&#xff0c;安全可信的通信云服务商融云&#xff0c;携手业内权威研究机构艾瑞咨询联合发布《2022 年中国政企数智办公平台行业研究报告》&#xff08;下简称《报告》&#xff09;&#xff0c;回顾政…

Kotlin高仿微信-第28篇-朋友圈-预览图片、预览小视频

Kotlin高仿微信-项目实践58篇详细讲解了各个功能点&#xff0c;包括&#xff1a;注册、登录、主页、单聊(文本、表情、语音、图片、小视频、视频通话、语音通话、红包、转账)、群聊、个人信息、朋友圈、支付服务、扫一扫、搜索好友、添加好友、开通VIP等众多功能。 Kotlin高仿…

ios照片误删怎么恢复,iphone已经删除的照片怎么恢复

苹果手机里面的重要照片被删除了&#xff0c;相信很多人都比较着急&#xff0c;想要想办法找回来。ios照片误删怎么恢复&#xff1f; 方法1.通过“最近删除”恢复照片 苹果删除的照片如何恢复&#xff1f;一般情况下&#xff0c;从苹果手机刚删除的照片会暂存在“最近删除”这…

使用MAT分析线上问题实战

概述 MAT&#xff0c;下载地址&#xff0c;Eclipse Memory Analysis Tools&#xff0c;一个分析Java堆数据的专业工具&#xff0c;可以计算出内存中对象的实例数量、占用空间大小、引用关系等&#xff0c;可得知哪些线程阻止垃圾收集器的回收工作&#xff0c;从而定位内存泄漏…

如何低成本减少企业知识流失?天翎知识文档系统+群晖NAS值得一试

编者按&#xff1a;知识管理可以减少企业知识流失&#xff0c;有效提高企业员工工作水平&#xff0c;增强企业综合竞争力。如何小成本做好企业知识管理呢&#xff1f;天翎知识文档系统群晖NAS值得一试。 关键词&#xff1a;标签分类&#xff0c;权限管理&#xff0c;在线预览&…

Git学习

Git是什么 Github作为最大的代码托管平台&#xff0c;是基于Git开发的 Git是最优秀的版本控制工具 iCode是基于Git的代码托管平台 版本控制&#xff1a;是对软件开发过程中各种程序代码&#xff0c;配置文件&#xff0c;说明文档等。 版本控制系统&#xff1a;集中式、分布式 …

在Mac中管理多版本 java——安装和使用 jenv

jenv 的 github 地址:https://github.com/jenv/jenv 安装 $ brew install jenv安装成功后需要进行一下简单的配置,让它可以起作用 使用Bash的情况$ echo export PATH="$HOME/.jenv/bin:$PATH" >> ~/.bash_profile $ echo eval "$(jenv init -)" &…

MAC 搭建vue开发环境,配置环境变量

1.官网下载nodejs安装包 http://nodejs.p2hp.com/ 下载完成后安装&#xff0c;一直点击下一步即可 2.自定以配置全局模块路径和缓存路径 先自己找一个路径创建两个文件夹&#xff0c;node_cache 和 node_global 打开终端&#xff0c;执行一下俩条命令,注意引号中的路径要换…

图纸识别自动生成BOM清单的方法

01 方案应用领域及行业 高端装备制造业行业、离散型制造业、电气机械和器材制造业等。 02 方案应用场景 某特变电工公司是国内输变电行业的核心骨干企业&#xff0c;每年生产产能巨大&#xff0c;拥有海量的技术图纸&#xff0c;因此技术人员人工拆解设计图纸的工作难度系数大…

【优化调度】基于改进遗传算法的公交车调度排班优化的研究与实现(Matlab代码实现)

目录 1 概述 2 运行结果 3 参考文献 4 Matlab代码 1 概述 本文对当前公交企业调度系统进行了分析&#xff0c;建立了公交排班的数学模型。本文基于数据挖掘分析的结果上&#xff0c;使用截面客流量数据对模型进行约束&#xff0c;得出了公交客流出行的空间分布规律。再以…

软件测试1

这里写自定义目录标题软件测试的定义1、软件测试的目的2、软件测试的要求3、测试与开发的模型3.1 测试的工作流程3.2 开发模型3.2.1 瀑布模型3.2.2 增量模型3.2.3 快速模型3.2.4 其他模型3.3 测试模型4、测试与开发的关系5、软件测试分类软件测试的定义 找Bug,发现缺陷。使用人…

全球科学家给孩子的stem课【001-046】mp3合集

全球科学家给孩子的stem课【001-046】mp3合集&#xff0c;适合给孩子们启蒙教育使用。 一只蚂蚁走丢后的8种可能结局.mp3 下雨天清爽的味道&#xff0c;来自尸体和臭气.mp3 为什么光的三原色是红绿蓝&#xff0c;绘画的三原色又成了红黄蓝&#xff1f;.mp3 为什么剖腹产的孩子…

患有癌症的心力衰竭患者LVAD植入前景可期

相关调查显示&#xff0c;患有终末期心力衰竭的癌症患者&#xff08;有癌症史&#xff09;正在增加&#xff0c;但其进行心脏移植的可能性不大&#xff0c;而左心室辅助装置&#xff08;LVAD&#xff09;是一种可选择的替代策略。近日&#xff0c;发表于Circulation: Heart Fai…

[附源码]SSM计算机毕业设计疫情期间回乡人员管理系统JAVA

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…