【Linux取经路】初识信号

在这里插入图片描述

文章目录

  • 一、人眼中的信号 VS 进程眼中的信号
  • 二、ctrl+c 终止一个前台进程
  • 三、查看信号信息
    • 3.1 Core dump——核心转储功能验证
  • 四、信号的处理方式
  • 五、ctrl+c 被解释成2号信号验证
    • 5.1 signal——设置自定义捕捉方法
  • 六、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:查看系统的信号列表。

image-20240307222045146

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

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

image-20240308111852235

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

在这里插入图片描述

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

image-20240308154003879

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

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

#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;
}

image-20240308154955145

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

#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;
}

image-20240308160411555

四、信号的处理方式

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

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

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

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

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

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

image-20240307223621300

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

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

// 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;
}

image-20240307224816142

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

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

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

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

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

image-20240308084143944

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

image-20240308085252928

七、异步、软中断

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

八、结语

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

在这里插入图片描述

上一篇:4.00003 postgresql的内存管理 - 我是怎么获得可用内存的?