【Linux取经路】初识信号

文章目录

  • [一、人眼中的信号 VS 进程眼中的信号](#一、人眼中的信号 VS 进程眼中的信号)
  • [二、ctrl+c 终止一个前台进程](#二、ctrl+c 终止一个前台进程)
  • 三、查看信号信息
    • [3.1 Core dump------核心转储功能验证](#3.1 Core dump——核心转储功能验证)
  • 四、信号的处理方式
  • [五、ctrl+c 被解释成2号信号验证](#五、ctrl+c 被解释成2号信号验证)
    • [5.1 signal------设置自定义捕捉方法](#5.1 signal——设置自定义捕捉方法)
  • [六、ctrl+c 是如何变成信号的?](#六、ctrl+c 是如何变成信号的?)
  • 七、异步、软中断
  • 八、结语

一、人眼中的信号 VS 进程眼中的信号

人眼中的信号

  • 以人为例,我们认识信号,一定是因为有人教我们,我们记住了。**认识信号包括:识别信号和知道信号的处理方法。**例如,我们可以识别红灯这个信号,并且知道红灯亮了就不能再过马路了。
  • 即便是现在没有信号产生,我们也知道产生信号后,我们应该做什么。
  • 信号产生了,我们可能并不立即处理这个信号,而是在合适的时机去处理,因为我们当前可能正在做更重要的事情,所以在信号产生后一直到信号处理前,有一个时间窗口,在这个时间窗口内,我们必须记住信号的到来

进程眼中的信号

  • 进程必须能够识别、处理信号,即使信号没有产生,也要具备处理信号的能力。信号的处理能力,属于进程内置功能的一部分。
  • 进程即便是没有收到信号,也能知道哪些信号该怎么处理。
  • 当进程真的收到了一个具体的信号的时候,进程可能并不会立即处理这个信号,在合适的时候处理。
  • 一个进程必须当信号产生了,到信号开始被处理,就一定会有时间窗口,进程具有临时保存哪些信号已经发生了的能力。

二、ctrl+c 终止一个前台进程

现象ctrl+c 可以杀掉一个前台 进程。Linux 中,一次登陆中,一个终端,一般会配上一个 bash ,每一个登陆,只允许,值允许一个进程是前台进程,可以允许多个进程是后台进程。bash 也是进程,在向 bash 输入指令之前,bash 就是前台进程,此时如果在 baash 中输入一个指令 ./myprocess,以我们自己写的可执行程序为例,该程序中有一个死循环,一直向显示器打印,那么该程就会变成前台进程,bash 就会变成后台进程,此时再向 bash 中输入指令是没有任何反应的。前台进程和后台进程的一个本质区别是,谁来获取键盘输入,只有前台进程才能拿到键盘输入。 ./myprocess & 此时 myprocess 是一个后台进程。ctrl+c **本质是被进程解释为收到了 2 号信号。**进程收到2号信号的默认动作就是终止自己。

三、查看信号信息

pidof myprocess:显示操作系统中所有 myprocess 进程的 pid。

pidof myprocess | xargs kill -9:杀掉当前操作系统启动的所有 myprocess 进程。

kill -l:查看系统的信号列表。

没有0、32、33号信号,一共有62个信号。1-31号称为普通信号;34-64号称为实时信号,进程一旦收到实时信号,必须立即处理。信号本质上就是数字,右边的字符是信号的名称,在操作系统的内核中,信号一定是以宏定义的方式提供的,这些宏定义在 signal.h 中可以找到。

man 7 signal:查看信号的详细说明。

其中 TermCore 表示终止;Ign 标记忽略;Cont 表示继续;Stop 表示暂停。

其中 core dump 标志就是用来区分 TermCore 的。云服务器的 Core dump 功能默认是关闭的,可以通过 ulimit -a 指令来查看当前系统的所有资源限制。

可以通过 ulimit -c size、 去设置它的大小为 size,如果 size > 0 就表示开启 Core dump 功能。

3.1 Core dump------核心转储功能验证

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

using namespace std;

int main()
{
    pid_t id = fork();
    if(id == 0)
    {
        // child
        int cnt = 500;
        while(cnt--)
        {
            cout << "I am child, my pid is: " << getpid() << endl;
            sleep(1);
        }

    }
    else if(id > 0)
    {
        // father
        int status = 0;
        pid_t ret = waitpid(id, &status, 0);
        if(ret == id)
        {
            cout << "wait " << id << "success, " << "exit code: " << WEXITSTATUS(status) << ", exit signalnum: " << WTERMSIG(status) << ", core dump: " << ((status >> 7)&1) << endl; 
        }
    }
    return 0;
}

可以看到,Term 对应的 core dump 标志位是 0;Core 对应的 core dump 标志位是 1。前者表示正常终止,后者表示异常终止。打开系统的 core dump 功能,一旦进程出现异常,操作系统会将进程在内存中的运行信息,给 dump (转储)到进程的当前目录中,形成 core.pid 文件,core dump 被称作核心转储core.pid 文件中详细记录了程序的异常原因,可以直接帮我们定位到出错行。

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

using namespace std;

int main()
{
    int a = 10;
    int b = 0;
    a /= b;
    return 0;
}

四、信号的处理方式

收到信号有以下三种处理方式:

  • 默认动作:人看到红灯,默认动作就是站在原地,等待红灯结束。
  • 忽略:人看到红灯之后,不管红灯,继续往前走。
  • 自定义动作(信号的捕捉):人看到红灯后,开始唱歌。

进程收到一个信号后,它的处理动作只能在上面这三个动作中进行三选一。

五、ctrl+c 被解释成2号信号验证

5.1 signal------设置自定义捕捉方法

signal------设置进程对某个信号的自定义捕捉方法 :当进程收到 signum 信号的时候,去执行 handler 方法。

  • signum:信号的编号。
  • handler:一个函数指针,指向信号的自定义捕捉动作。
  • sighandler_t:一个函数指针类型,指向一个返回值为 void 参数为 int 的函数,这个参数值就是收到的信号。为什么要有这个参数呢?因为同一个自定捕捉方法,可以捕捉多个不同的信号,所以在该方法中需要知道是收到哪个信号才来执行的该方法。

signal 函数只需要设置一次,往后都有效。sighandler_t 函数只会在收到对应的信号后才会执行,假如程序一直没有收到对应的信号,那么该函数就一直不会被调用。

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

using namespace std;

void myhandler(int signo)
{
    cout << "process get a signal: " << signo << endl;
}

int main()
{
    signal(2, myhandler);
    while(true)
    {
        cout << "Hello Linux" << endl;
        sleep(1);
    }
    return 0;
}

进程收到信号后的默认动作和自定义捕捉动作只能执行一个,设置了自定义捕捉动作,默认动作就不再执行了。

六、ctrl+c 是如何变成信号的?

由于进程无法直接访问硬件资源,所以键盘被按下,一定是操作系统先知道的。

OS 怎么知道键盘上有数据了?

最简单的方法就是,操作系统定期的去检查键盘文件。把键盘数据读到操作系统当中,本质上是将键盘外设(文件)中的数据,拷贝到内核中的文件页缓冲区中。因为操作系统中有大量的外设,如果都采用定期检查的方法,去判断外设中是否有数据是非常浪费时间的,因此这种方法不可取。在数据层面,CPU 是不会直接和外设打交道的,但是在控制层面,当外设有数据了,外设可以给 CPU 发送硬件中断 。 中断有自己的中断号,CPU 通过中断号来判断该中断是哪个外设发送的。之后 CPU 会以这个中断号为下标,去操作系统的中断向量表中执行对应的方法。 操作系统在将键盘文件中的数据拷贝到操作系统之前,会先判断输入的是数据还是控制,如果是控制,ctrl+c 就是控制,会被操作系统转换成2号信号发送给进程。

键盘和显示器有各自独立的文件页缓冲区

七、异步、软中断

信号的产生和代码的执行是异步的,所谓异步就是在我们代码执行的任何时候都有可能收到信号。信号是进程之间事件异步通知的一种方式,属于软中断。

八、结语

今天的分享到这里就结束啦!如果觉得文章还不错的话,可以三连支持一下,春人的主页还有很多有趣的文章,欢迎小伙伴们前去点评,您的支持就是春人前进的动力!

相关推荐
_wyt00113 小时前
洛谷 B3930 [GESP202312 五级] 烹饪问题 题解
c++·gesp
大树8815 小时前
金刚石散热越强,管路越先见顶
大数据·运维·服务器·人工智能·ai
摇滚侠15 小时前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
霸道流氓气质16 小时前
领域驱动设计(DDD)在 Spring Boot 微服务中的实践指南
运维·spring boot·微服务
bush416 小时前
嵌入式linux学习记录十四、术语
linux·嵌入式
载数而行52016 小时前
Linux 11 动态监控指令top
linux
小宇宙Zz16 小时前
Maven依赖冲突
java·服务器·maven
Inhand陈工17 小时前
基于台达PLC与映翰通IG502的智慧水产养殖精准投喂与远程运维解决方案
运维·人工智能·物联网·阿里云·信息与通信
玖玥拾17 小时前
C/C++ 数据结构(七)栈、容器适配器
c语言·数据结构·c++··容器适配器
酣大智17 小时前
ARP代理--工作原理
运维·网络·arp·arp代理