Linux进程信号(1):信号概述,信号产生part 1

本章目标

1.信号概念概述

2.信号产生

1.信号概念概述

在生活中,我们有很多地方都有信号这个概念,红绿灯,红灯的时候要停车,绿灯的时候要行驶.对于信号这样一个概念来说.我们时提前就知道信号的,也就是我们能够识别出信号,知道红灯和绿灯之间的区别并且知道当信号产生的时候如何去进行处理结果.

我们再举个例子

我们收快递.快递站能够对快递进行区分.它会先积压在快递站,当在合适时机当快递员把快递给你的时候,你能够对其进行处理.但是对于快递什么时候会被送到我们是不知道,整个过程对于你来说是一个黑盒,这个过程来说快递员和你是异步的.当收到快递你可以对其进行不同方式的处理你可以拆快递(默认),忽视它(忽略),把它送人(自定义)

我们在Linux当中的信号同样如此,它的实现与上面所说是异曲同工的

对于信号这个概念来说,我们曾经接触过,我们可以通过kill -9这个信号杀死一个进程

向该进程发送9号信号.

对于信号来说,进程能够进行识别.是因为在进程创建之前,它就已经存在了.

我们处理进程的方式是终止进程.这个是9号信号的默认动作

在linux当中 我们可以通过man 7 signal,这个指令查看具体的进程对应默认的处理方法,以及可以通过 kill -l 这个方式查看linux当中所以的进程信号

man 7 sigal 可以通过这个指令找到这张表,

通过kill -l找到信号一共有如上1-64,34以上的信号为实时信号在如下的章节,我们并不会进行讨论.我们在这里讨论的仅为1-31号信号.

1,1进程信号的声明周期

对于上面的例子,我们可以高度总结出一个进程的声明周期

信号产生之后要对信号进行识别,信号识别这个操作,在上面已经说明,它是提前设置好的,可以把它理解为进程的特性.我们可以在内核的sigal.h找到对应的定义

上面的第一张图来自2.6.0的内核,第二张图来自0.11的内核

上面的例子足够可以进行说明进程是认识信号的了,而如何进行区分.在进程当中是通过位图来进行实现的

我们可以通过位运算的方式确定信号的存在 &.

而信号产生有不同的方式,因为要适应不同的场景.

提一个最简单的场景,我们之前可以通过kill 命令发送9号信号杀死进程.

而在操作系统当中,我们可以通过不同的发送去发送信号,我们一会会详细介绍.

对于信号来说,它会在合适时机进行处理,具体是什么合适的实际要在我们说内核态与用户态的时候再来谈.而对于这些没有被处理的信号,他会保存在pending表当中,而在老内核时单独的一个变量.我们后面也会对其进行详细介绍

1.2信号的处理方式(概述)

对于信号处理方式

一般分为3种

默认(一般都是终止进程)

忽略

自定义

自定义我们又叫做信号捕捉.对于进程信号来说,这三种方式都是归于信号的处理.

默认很好理解,就是我们预先设定好的处理方法.

忽略就是对当前信号的产生不做处理.

而自定义就是我们可以对其进行覆盖,用我们自己想用的处理方法去处理信号.

默认的效果很好见到.但是对于忽略和自定义,我们要介绍一个系统调用

对于这个signal 函数,第一个参数我们可以直接传宏,我们在前面的展示中内核的种是如何进行定义的,我们已经知道了.同样也可以直接传数字而对于第二个参数,这个是一个函数指针类型,它就是我们的自定义函数

对于这个参数,我们还可以传两个宏进去

SIG_DFL 和SIG_IGN 这两个宏.

它是被这个指针类型进行强转了.

对于一个函数指针来说,它放的是一个函数的起始地址.而对于这个地址来说,它就不可能为0 或者为1.当这个signal 内部它会先确定是不是这几个特殊值.然后才回去回调你给的自定义函数.

在这里SIG_DEL 它是表示的是一个函数的默认行为.

SIG_IGN表示当前进程对这个信号的产生并不会做出反应,会直接忽略

在介绍完它的参数之后,我们再回来看这个函数的返回值,它同样是一个函数的指针类型

它会返回一个,我们在捕捉前的函数.方便我们后续进行恢复.

为了方便,我们后续的测试,我们在这里介绍两个可以由键盘产生的信号 2号信号和3号信号.他们都可以通过ctrl +c 以及ctrl +\的方式进行触发,默认动作都是终止进程.但是二者有一个核心区别是能够触发核心转储,对于这点,我们也会再后面介绍.

cpp 复制代码
#include<iostream>
#include<signal.h>
#include<sys/types.h>
#include<unistd.h>

void hander(int signo)
{
    std::cout<<"sigal"<<std::endl;
}

int main()
{
    signal(2,hander);
    while(1)
    {
        std::cout<<"father "<<getpid()<<std::endl;
        sleep(1);
    }
    return 0;
}

很容易就能够看到,当我们对其进行ctrl +c触发二号信号的时候它直接打印,同时终止进程的默认行为被直接覆盖了,成功执行了信号捕捉.

我们发现当我们信号被覆盖了之后,原先信号的行为并不会被执行,也就不会退出了,如果我们讲所以信号都进行自定义,那我们的进程就不会通过信号终止进程退出了吗?

对于这个问题,我们要先来提出结论.并不会,在我们的操作系统前31号信号中.9号信号和19号信号并不会被覆盖也同样不能被忽略.

我们下面修改案例

cpp 复制代码
void hander(int signo)
{
    std::cout<<"sigal"<<std::endl;
}

int main()
{
    for(int i = 31;i>=0;i--)
    signal(i,SIG_IGN);
    while(1)
    {
        std::cout<<"father "<<getpid()<<std::endl;
        sleep(1);
    }
    return 0;
}

我们能够看到我们发送1号信号并不会被执行,但是9号信号能够直接杀死进程.同理19同样如此.在这里不做过多的重复实验

,但是对于有些信号并不会被覆盖.我们称这类捕捉之后仍然会触发的信号叫做核心信号行为.我们再进程信号捕捉的同时并不会直接覆盖/二者会同时运行这类信号有18号信号以及6号信号.我们会再函数产生信号的时候详细介绍.

我们再看看信号忽略

并没有反应.这就是SIG_IGN 信号忽略

对于信号来说,是谁再执行信号处理?

直接给出结论,是当前进程.我们可以在信号捕捉的自定义的函数打印pid就可以验证这件

cpp 复制代码
#include<iostream>
#include<signal.h>
#include<sys/types.h>
#include<unistd.h>

void hander(int signo)
{
    std::cout<<"sigal"<<getpid()<<std::endl;
}

int main()
{
   // for(int i = 31;i>=0;i--)
    signal(2,hander);
    while(1)
    {
        std::cout<<"father "<<getpid()<<std::endl;
        sleep(1);
    }
    return 0;
}

通过上面的例子,我们对信号下一个定义

信号本质上是外部或者其他人,或者硬件对进程发送的一种异步的事件机制

2.信号产生

我们现在根据信号的整个周期来进行介绍信号各个部分分别做的工作.

对于信号来说,我们分为三个部分来进行介绍.

我们先介绍信号的产生,对于信号这个来说,我们可能再各种地方出现信号这种机制.

所以为了应用于不同的信号产生的场景,信号的产生方式也有不同.

2.1通过终端按键产生

这个就是我们上面介绍的例子.通过ctrl+c触发二号信号终止进程,ctrl+\触发3号信号终止

cpp 复制代码
#include<iostream>
#include<signal.h>
#include<sys/types.h>
#include<unistd.h>

void hander(int signo)
{
    std::cout<<"sigal"<<std::endl;
}

int main()
{
    signal(2,hander);
    while(1)
    {
        std::cout<<"father "<<getpid()<<std::endl;
        sleep(1);
    }
    return 0;
}

再这里就不过多阐述这个问题,我们在这里要引出的是另外一套东西

我们的通过按键触发信号,本质上是我们的键盘向内存中当中写数据.

那我们的在前面介绍过,信号本身是一种异步的事件机制,我们的os是不知道什么时候键盘上有数据的.我们的操作系统一般在这里有两个做法

1.轮询检查,我们隔一段时间去检查

但是,这种做法消耗巨大,需要我们轮询去检查各个外设文件上是否有数据存在.

不过就冯诺依曼体系所产生的问题,cpu本身是和外设有速度代差的.大部分时间不会有任何数据

2.中断,由硬件触发,让os检查

我们直接通过一种机制中断,给os发信号通知它去进程检查对应的外设文件,由硬件触发,那它就一定有数据,这样依赖就能够减少浪费的系统资源了

对于中断是什么,我们暂时不做介绍.我们会再信号处理的章节的介绍

在这里提出来只是为了保证逻辑自洽

在这里还要提出两个问题

1.对于按键触发信号,它只能够通过向前台进程发送信号,但是对于后台进程来说,它是收不到信号的,最本质的原因是因为后台进程收不到键盘的按键

2.为什么我们的bash不会受到信号?

因为bash忽略了所有的信号,它自身的终端是收不到信号的,但是我们可以通过另一个终端杀死bash

2.2通过系统命令向进程发送信号

这里就很明显了,我们之前也做过很多次

直接通过kill命令向队应的进程发送信号.

这个是我们今天介绍的最简单的.

我的机器上刚好跑着一个进程直接通过 kill -9 杀掉

相关推荐
colicode1 小时前
C++语音验证码接口API示例代码详解:高性能C++语音校验接入Demo
前端·c++·前端框架·语音识别
阿i索1 小时前
流对象输入输出(cin/cout)
c++·笔记·学习
载数而行5201 小时前
数据结构系列15之图的存储方式2
c语言·数据结构·c++
王老师青少年编程1 小时前
2021年信奥赛C++提高组csp-s初赛真题及答案解析(阅读程序第1题)
c++·题解·真题·初赛·信奥赛·csp-s·提高组
暴力求解1 小时前
Linux---进程(一):初识进程
linux·运维·服务器
淡唱暮念2 小时前
Linux系统使用夸克网盘CLI上传服务器数据至网盘教程,解决大数据备份苦恼
linux·服务器·ubuntu
REDcker2 小时前
curl完整文档
c++·c·curl·服务端·后端开发
小比特_蓝光2 小时前
STL小知识点——C++
java·开发语言·c++·python
阿莫西林夹馍2 小时前
ubuntu安装gitlab
linux·ubuntu·gitlab