【Linux】第三十六站:信号

2024-01-28 23:12
文章标签 linux 信号 第三十六

本文主要是介绍【Linux】第三十六站:信号,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 一、信号的概念
    • 1.信号概念
    • 2.前台与后台进程
    • 3.信号的处理
    • 4.硬件层面
    • 5.信号与我们的代码是异步的
  • 二、信号的产生
    • 1.产生的方式
    • 2.键盘组合键
    • 3.kill命令
    • 4.系统调用
      • 4.1 kill系统调用
      • 4.2 raise
      • 4.3 abort
    • 5.异常软件条件
      • 5.1 异常产生信号
      • 5.2 alarm(软件条件产生信号)
    • 6.core dump

一、信号的概念

1.信号概念

我们常见的信号有信号弹、下课上课铃声、求偶、红绿灯、快递发短信取件码等等…

a.那么我们是怎么认识这些信号的??

当然是有人教我们,最后我们记住了

这个认识,首先我们要识别信号,其次还要知道信号的处理方法。最后记住他们

b.即便是我们现在没有信号产生,我也知道信号产生了之后,我该干什么

c.信号产生了,我们可能并不立即处理这个信号,在合适的时候,因为我们可能正在做更重要的事情。 — 所以,信号产生后一直到信号处理时,中间一定有一个时间窗口。在这个时间窗口内,我们必须记住信号到来!

上面这些我们指代的就是进程!

所以

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

如下图所示,我们前面所作的相当于是一个信号的预备部分

image-20240123232831963


2.前台与后台进程

我们还记得,我们使用CTRL + C可以杀掉前台进程。(像如下所示的,就是前台进程,该进程运行时,shell不会接收其他命令了)

image-20240124221138981

但是如果我们这样做,它就是一个后台进程了,即在程序后面加上一个&即可,而且我们还发现,我们直接CTRL + C已经杀不掉它了。

image-20240124221652570

在我们的Linux中,一次登录中,一个终端,一般会配上一个bash。每一个登录只允许一个进程是前台进程,可以允许多个进程是后台进程。

如果我们要杀掉后台进程,我们只能使用kill -9命令了

image-20240124222011674

一般来说,前台进程和后台进程的区别就是谁能获取键盘输入

键盘输入首先是被前台进程收到的

那么既然一开始bash是前台进程,那么为什么使用CTRL+C时候,bash不退出呢?

这当然是因为bash在里面对这个信号做了特殊处理

CTRL +C 本质是被进程解释成为收到了信号,2号信号

我们知道我们的系统一共有62个信号(没有0号,32号,33号)

image-20240124235545846

我们将前31个信号称之为普通信号。后面的34~64我们称之为实时信号

一旦信号产生,不立即处理就是普通信号,立即处理是实时信号

这些信号本质就是一些数字,在linux中它们是以宏的方式定义的,就是这些数字。

3.信号的处理


信号的处理方式

  1. 默认动作
  2. 忽略
  3. 自定义动作

我们现在可以验证一下,进程收到2号信号的默认动作,就是终止自己!

我们先来看一下这个函数

image-20240125000914422

#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);

这个函数的作用是设置对于signum信号的处理方法,处理方法为handler

这个signal函数是一个系统调用

它可以修改进程对于特定信号的处理动作

我们用如下代码来验证

#include <iostream>
#include <unistd.h>
#include <signal.h>
using namespace std;void myhandler(int signo)
{cout << "process get a signal: " << signo << endl;
}
int main()
{signal(SIGINT, myhandler);while(true){cout << "I am a crazy process" << endl;sleep(1);}return 0;
}

运行结果为

image-20240125001657759

可见我们已经证明了2号信号的默认动作

我们也可以设置让2号信号可以退出

image-20240125002151798

运行结果为

image-20240125002215607

对于这个signal函数,只需要设置一次即可,往后都有效

只有收到了对应的信号,才会调用这个方法

注意:不是所有的信号都可以自定义的。有些信号不能自定义

4.硬件层面

键盘数据是如何输入给内核的,CTRL + C又是如何变成信号的?

键盘被摁下,肯定是OS先知道的

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

如下图所示,是冯诺依曼体系结构

image-20240125003524986

我们知道,Linux下一切皆文件。键盘也是,它也有自己对应的struct file。以及缓冲区

image-20240125003730349

往键盘输入数据本质就是把输入的数据拷贝到缓冲区上。所以操作系统就知道了

所以我们就可以用read,write通过文件的方式把数据读到进程当中

image-20240125003934697

那么操作系统要把键盘上的数据拷贝到缓冲区中。在这个过程中,操作系统怎么知道键盘上有数据了?

其实CPU上是有很多针脚的。我们的CPU是直接插到主板上的。而键盘是可以间接的和CPU直接物理上连接到的。虽然CPU不从键盘读数据。但是键盘可以给CPU发送一个硬件中断。

也就是说,一旦外设有数据,就可以给CPU发送一个中断,从而让操作系统去完成文件拷贝。键盘、网卡等都可以给CPU发送中断。

可是外设一多,CPU如何知道是谁给发送的中断呢?

这就会有一个中断号的概念。他们会通过这些针脚,直接将中断号发送给CPU

一旦硬件CPU知道键盘上有数据了。

CPU的寄存器凭什么能保存数据呢??

其实这个本质就是充放电的过程。如果是高电平代表充电了,就有1了。

image-20240125005906919

在软件层面上,操作系统一启动,就会形成一张,中断向量表。里面放的是方法的地址。这些方法是直接访问外设的方法—主要是磁盘,显示器,键盘

image-20240125010244301

然后最后这个读取键盘的方法,才是将键盘的数据放到缓冲区的方法

image-20240125010528251

所以其实整个流程就是,键盘一旦有数据,会通过中断将中断号给CPU,CPU会利用这个中断号,让操作系统直接去通过中断向量表,找到对应的读取键盘的方法,然后通过这个方法就会让数据从键盘拷贝到这个文件缓冲区上。

所以键盘这个外设是通过中断来工作的。这个就是硬件中断

而我们前面所说的信号,也是通过一堆数字来进行控制。这两者其实比较相似,但是没有关系。一个是软硬件结合的,一个是纯软件行为。

我们所用的信号,就是用软件的方式,对进程模拟的硬件中断

当我们键盘读取的是CTRL + C这样的组合键的时候,操作系统其实还会对键盘上的数据进行判断。判断是数据还是控制,如果是控制,比如CTRL+ C会把这个转化为2号信号发送给进程。而不是放到缓冲区中。所以进程就收到了2号信号

像我们之前所谓的输入数据后往显示器上回显的过程其实是这样的,先将数据放到键盘的缓冲区,然后将键盘的缓冲区的数据放到显示器的缓冲区,最后就能输出了

image-20240125012101505

如果这里不给显示器的缓冲区,就是不回显

当然,在这个过程中,也会有其他的进程给显示器的缓冲区上放数据

image-20240125012217825

所以即便在显示器上的是乱的,但是我们还是能成功的执行指令

5.信号与我们的代码是异步的

信号的产生的和我们自己的代码的运行是异步的

同步就是发生一件事后等这件事发生完了才继续做我们的事情

异步就是这件事发生后我们不管这个事情,继续做我们的事情

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

二、信号的产生

1.产生的方式

  1. 键盘组合键
  2. kill命令
  3. 系统调用
  4. 异常软件条件

以上是信号产生的方式!但是无论信号如何产生,最终一定是谁发送给进程的?

当然是OS

那么是为什么呢?

操作系统是进程的管理者!

2.键盘组合键

比如CTRL+C是2号信号

我们可以试一下捕捉三号信号

使用CTRL + \即可捕捉3号信号

#include <iostream>
#include <unistd.h>
#include <signal.h>
using namespace std;void myhandler(int signo)
{cout << "process get a signal: " << signo << endl;//exit(1);
}
int main()
{//signal(SIGINT, myhandler);signal(3, myhandler);while(true){cout << "I am a crazy process" << endl;sleep(1);}return 0;
}

image-20240125013753341

CTRL + Z是19号信号

#include <iostream>
#include <unistd.h>
#include <signal.h>
using namespace std;void myhandler(int signo)
{cout << "process get a signal: " << signo << endl;//exit(1);
}
int main()
{//signal(SIGINT, myhandler);//signal(3, myhandler);signal(19, myhandler);while(true){cout << "I am a crazy process" << endl;sleep(1);}return 0;
}

image-20240125014500820

如下所示,我们似乎会发现,我们上面似乎并没有将19号信号用自定义的方法进行处理

其实这是因为不是所有的信号,都是可以被signal捕捉的,比如19,9号信号

我们可以用下面的代码进行测试。这里就不做演示了

#include <iostream>
#include <unistd.h>
#include <signal.h>
using namespace std;void myhandler(int signo)
{cout << "process get a signal: " << signo << endl;//exit(1);
}
int main()
{//signal(SIGINT, myhandler);//signal(3, myhandler);//signal(19, myhandler);for(int i = 1; i <= 31; i++){signal(i, myhandler);}while(true){cout << "I am a crazy process" << endl;sleep(1);}return 0;
}

19号是用来暂停进程的,9号信号是用来杀掉进程的。

这两个都是跟执行相关的。这是为了预防进程出现意外所设计的。不能被捕捉的

3.kill命令

kill -信号 进程pid

4.系统调用

4.1 kill系统调用

image-20240125151455582

#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);

它的两个参数分别是pid和信号的编号。与命令行中的kill是很相似的

如果成功返回0,失败返回-1

我们可以简单的利用这个系统调用接口实现一个kill命令

#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <string>
#include <sys/types.h>
using namespace std;void Usage(string proc)
{cout << "Usage:\n\t" << proc << "signum pid\n\n";
}int main(int argc, char* argv[])
{if(argc != 3){Usage(argv[0]);exit(1);}int signum = stoi(argv[1]);pid_t pid = stoi(argv[2]);int n = kill(pid, signum);if(n == -1){perror("kill");exit(2);}return 0;
}

测试结果如下所示

image-20240125153352529

4.2 raise

image-20240125153520451

#include <signal.h>
int raise(int sig);

它的作用,发送一个信号给调用本方法者

我们可以用如下代码进行测试

#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <string>
#include <sys/types.h>
using namespace std;
void myhandler(int signo)
{cout << "process get a signal: " << signo << endl;exit(1);
}
int main()
{signal(2, myhandler);int cnt = 5;while(true){cout << "I am a process, pid: " << getpid() << endl;sleep(1);cnt--;if(cnt == 0) raise(2);}return 0;
}

运行结果为

image-20240125154306514

这个raise相当于

kill(getpid(), 2);

4.3 abort

image-20240125154538468

它的作用是引起一个正常的进程直接终止

它相当于给自己发送一个6号信号

我们先用下面代码进行测试

int main()
{int cnt = 5;while(true){cout << "I am a process, pid: " << getpid() << endl;sleep(1);cnt--;if(cnt == 0) abort();}return 0;
}

image-20240125155156642

如果我们继续将代码改为下面的,让6号信号可以被捕捉

void myhandler(int signo)
{cout << "process get a signal: " << signo << endl;//exit(1);
}
int main()
{signal(6, myhandler);int cnt = 5;while(true){cout << "I am a process, pid: " << getpid() << endl;sleep(1);cnt--;if(cnt == 0) abort();}return 0;
}

那么结果为

image-20240125155415576

我们会注意到,虽然我们捕捉了以后,并没有将他给终止,但是abort依然将他给终止了。

我们可以再来观察一下

在下面的代码中,我们不让他自己abort了,我们现在在命令行上发送6号信号

void myhandler(int signo)
{cout << "process get a signal: " << signo << endl;//exit(1);
}
int main()
{signal(6, myhandler);int cnt = 5;while(true){cout << "I am a process, pid: " << getpid() << endl;sleep(1);cnt--;//if(cnt == 0) abort();}return 0;
}

image-20240125155613395

我们发现进程并没有被终止

所以其实是abort里面封装了一层,必须让进程终止

5.异常软件条件

5.1 异常产生信号

我们先使用如下代码

#include <iostream>
#include <signal.h>
#include <unistd.h>
using namespace std;int main()
{cout << "div before" << endl;sleep(1);int a = 10;a /= 0; cout << "div after" << endl;sleep(1);return 0;
}

运行结果为

image-20240125162723106

像这种情况就是收到了信号了

收到的是8号信号

我们可以用七号手册

man 7 signal

往下翻就可以找到这个信号详情了,可以看到确实是八号信号

image-20240125163007468

如果我们用下面的代码进行测试

#include <iostream>
#include <signal.h>
#include <unistd.h>
using namespace std;
void handler(int signo)
{cout << "get a signal, number: " << signo << endl;
}int main()
{signal(SIGFPE, handler);cout << "div before" << endl;sleep(1);int a = 10;a /= 0; cout << "div after" << endl;sleep(1);return 0;
}

那么运行结果为。打印很多的收到八号信号

image-20240125164356868

而且这个过程中进程是不会退出的

image-20240125164730496

这里的都还是比较好解释的:

不过我们可能比较好奇的是为什么在这里他会一直发送这个八号信号呢?

即信号为什么会一直被触发?

我们先看下面的代码

#include <iostream>
#include <signal.h>
#include <unistd.h>
using namespace std;
int main()
{//signal(SIGFPE, handler);cout << "point error before" << endl;sleep(1);// int a = 10;// a /= 0; int* p = nullptr;*p = 10;cout << "point error after" << endl;sleep(1);return 0;
}

运行结果为

image-20240125170943491

这里也是代码没有跑完直接崩溃了,这个本质也是收到了信号。收到的是11号信号

我们在捕捉一下11号信号

#include <iostream>
#include <signal.h>
#include <unistd.h>
using namespace std;
void handler(int signo)
{cout << "get a signal, number: " << signo << endl;
}int main()
{signal(SIGSEGV, handler);cout << "point error before" << endl;sleep(1);// int a = 10;// a /= 0; int* p = nullptr;*p = 10;cout << "point error after" << endl;sleep(1);return 0;
}

运行结果为

image-20240125171249312

和前面十分类似,会不断的捕捉11号信号,而且还不会退出

所以进程收到异常信号,不一定会退出。但是一定会执行异常处理方法

虽然上面捕捉后一直在打印,没有退出,不过其实我们大概率还是要将他们给退出的。因为一直打印也没什么用

image-20240125171650007

运行结果为

image-20240125171721591

为什么 /0 ,野指针会给进程发送信号?导致进程崩溃?

这里是因为,除零,野指针会给系统带来问题,操作系统识别到这些问题,然后OS给进程发送的信号。信号的默认处理动作就是终止自己,所以就崩了

操作系统为什么能检测到除零,野指针呢?

首先如下所示,如果是除零错误

在CPU上有一个eip/pc寄存器可以用来记录当前执行的是哪一行代码

image-20240125172949278

还有一种寄存器是状态寄存器。它里面的每一位都有特定的含义

image-20240125173431114

其中有一个就是溢出标志位

一旦我们的代码除零了,这个溢出标志位就变为1了

像我们在CPU的这些数据,都是这个进程的上下文

image-20240125173701835

也就是说,这里虽然我们修改的是CPU内部的状态寄存器,但是这里只影响我们自己。不会影响其他进程,进程切换的时候,其他进程会将自己的上下文数据放上去

在这里操作系统一定会知道这里出错了。因为CPU是硬件,OS是硬件的管理者。

所以操作系统才会向进程发送信号


如果是野指针异常

如下图所示,在CPU里面有一个内存管理单元,因为直接查页表太慢了,所以有一个MMU硬件来进行查表。

一旦异常,也就是地址转化失败了。虚拟到物理转化失败了。

在CPU内还有一个寄存器,一旦转化失败了。它会把转化失败的虚拟地址放在这里

image-20240125180552673

也就是说,一旦转化失败,CPU也能识别


而且因为他们是用的不同的寄存器,所以CPU也能区分出来是哪种报错,操作系统也就知道了


这里我们进程出异常以后,我们本应该退出。但是如果我们非要不退出。

意味着这个进程一直被调度运行。

硬件一直存在这个问题。我们也没有修正。随着我们的调度。操作系统一直在检测到这个异常,然后我们也一直在捕捉这个信号。所以就一直打印。


所以捕捉异常其实就不是让我们不让进程退出的,而是让我们知道我们这个进程是怎么死掉的

那么异常只能由硬件产生吗?

当然不是。

比如我们之前的管道,如果一开始读写端都打开,但是我们突然关闭了读端。那么写端进程就会被杀掉。会收到一个SIGPIPE(13)号信号。这就是一种软件异常。

也有的异常,操作系统只是会返回值出错的形式进行处理

image-20240125183437279

运行结果为

image-20240125183450658

5.2 alarm(软件条件产生信号)

image-20240126155127170

#include <unistd.h>
unsigned int alarm(unsigned int seconds);

alarm 系统调用用于设置一个定时器,当定时器计时器达到指定的时间时,内核会发送一个 SIGALRM 信号(14号信号)给调用进程。这可以用于实现定时器功能,例如在一定时间间隔内执行某个特定的操作或执行定时任务

seconds 参数表示定时器的秒数。如果 seconds 参数为非零值,表示设置定时器,在指定秒数后会发送 SIGALRM 信号给进程。如果 seconds 参数为零,则表示取消之前设置的定时器。

返回值是剩余的未完成的定时器秒数。如果之前有一个定时器已经设置,调用 alarm 会取消之前的定时器,并返回剩余的秒数。如果没有之前的定时器,或者之前的定时器已经到期,返回值为 0。

比如如下的代码中

#include <iostream>
#include <signal.h>
#include <unistd.h>
using namespace std;
int main()
{int n = alarm(5);while(1){cout << "proc is running..." << endl;sleep(1);}return 0;
}

运行结果为

image-20240126160304788

我们可以捕捉一下这个信号

#include <iostream>
#include <signal.h>
#include <unistd.h>
using namespace std;
void handler(int signo)
{cout << "get a signal, number: " << signo << endl;//exit(1);
}int main()
{   signal(SIGALRM, handler);int n = alarm(5);while(1){cout << "proc is running..." << endl;sleep(1);}return 0;
}

image-20240126160519144

这里并不是异常导致的,所以不会循环式的疯狂捕捉。

我们也可以下面这样做,就可以每隔三秒打印一次了

#include <iostream>
#include <signal.h>
#include <unistd.h>
using namespace std;
void handler(int signo)
{cout << "get a signal, number: " << signo << endl;int n = alarm(3);//exit(1);
}
int main()
{   signal(SIGALRM, handler);int n = alarm(3);while(1){cout << "proc is running..." << endl;sleep(1);}return 0;
}

image-20240126160814824

有了这个闹钟,我们就可以在执行主要任务的同时,去定时完成其他任务了。像下面这样即可

#include <iostream>
#include <signal.h>
#include <unistd.h>
using namespace std;void work()
{cout << "print log ..." << endl;
}void handler(int signo)
{work();//cout << "get a signal, number: " << signo << endl;int n = alarm(3);//exit(1);
}
int main()
{   signal(SIGALRM, handler);int n = alarm(3);while(1){cout << "proc is running..." << endl;sleep(1);}return 0;
}

关于它的返回值,我们可以这样做

#include <iostream>
#include <signal.h>
#include <unistd.h>
using namespace std;
void handler(int signo)
{cout << "get a signal, number: " << signo << endl;int n = alarm(5);cout << "剩余时间: " << n << endl; }int main()
{   signal(SIGALRM, handler);int n = alarm(50);while(1){cout << "proc is running..., pid: " << getpid() << endl;sleep(1);}return 0;
}

运行结果如下

image-20240126161754595

理解alarm

我们知道每个进程都可以使用alarm设置闹钟,所以操作系统中一定有大量的闹钟。所以操作系统要管理闹钟,所以闹钟就会用struct结构体描述,然后用链表等数据结构管理起来。这样所谓的闹钟管理就变成了对链表等的增删查改。

这个alarm结构体里面,一定有pid,或者pcb的指针。

操作系统底层中alarm的底层所用的时间用的是时间戳。这样最简单。

只要系统的当前时间大于等于里面设置的时间,就会发信号。

不过我们遍历链表的时候是比较浪费时间的。所以用一个小堆是最简单的。

6.core dump

我们可以看一下信号的详细信息手册

image-20240126163842284

我们可以注意到,常见的信号中大部分是终止信号的。还有一些是暂停(Stop),继续(Cont),忽略(Ign)等。

我们可以注意到终止信号中,有一些是Core,有一些是Term

在我们当时提到进程退出的时候,有一个这个字段core dump标志

image-20240126164328929

这个是用来表示是Core方式被杀还是Term方式

接下来我们使用这段代码来看看这个标志位

#include <iostream>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>using namespace std;
int main()
{pid_t id = fork();if (id == 0){int cnt = 500;while (cnt){cout << "I am a chid process, pid: " << getpid() << "cnt: " << cnt << endl;sleep(1);cnt--;}exit(0);}int status = 0;pid_t rid = waitpid(id, &status, 0);if(rid == id){cout << "child quit info, rid: " << rid << " exit code: " << ((status>>8)&0xFF) << " exit signal: " << (status&0x7F) << " core dump: " << ((status>>7)&1) << endl;}
}

如果被2号信号所杀,如下所示,这个标志为为0。而2号信号对应的Term方式

image-20240126171331475

如果我们用8号信号所杀,我们发现这个core dump 标志位还是0。但是8号信号对应的是Core。

image-20240126171709965

我们发现这两个都没有什么变化。

其实如果我们当前用的是虚拟机的话。如果进程崩掉的话。我们会发现当前目录会形成一个临时文件,这个临时文件是XXX.core文件。而我们当前的云服务器上会发现没有,这是因为默认云服务器上的core功能是被关闭的

那么这是为什么呢?是什么呢?怎么办呢?

我们使用下面这个命令,它可以查看系统当中一些标准的配置

ulimit -a 

image-20240126173808287

其中这个core file size选项,我们可以使用下面指令去查

ulimit -c

image-20240126174000025

我们可以看到结果为0,也就是说这个,core默认是被关掉的

我们可以使用下面指令去设置它

ulimit -c 10240(要设置的大小)

如下所示,也就是说,最大是这么大

image-20240126174145061

上面就是开启core

如果要关闭的话,我们可以直接设置为0

image-20240126174350554

当我们设置好了以后,我们再去使用2号信号和8号信号,结果就不一样了

image-20240126175209648

现在此时,八号信号的core dump就是1了

更关键的是,我们发现现在确实有这个临时文件了

image-20240126175425600

这个很明显就是刚刚的pid

打开系统的core dump功能

一旦进程出现异常,OS会将进程在内存中的运行信息,给我dump(转储)到进程的当前目录(磁盘),形成core.pid文件。

这就是核心转储(core dump)

而这个功能在云服务器上默认是被关闭的

那么为什么要进行核心转储呢?

上面的错误一定是运行时错误。

此时我们要知道什么原因错误了。在哪一行错误了。

所以我们要用core dump来进行定位我们的原始在运行时哪里出错了

我们可以先生成调试版本的可执行程序

#include <iostream>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>using namespace std;
int main()
{
int a = 10;
int b =0;a /= b;cout << "a = " << a << endl;
}

运行结果如下所示

image-20240126180623130

此时很显然,已经帮我们核心转储了

然后我们使用gdb调试时候,可以直接使用下面命令

core-file core.23652

image-20240126181049695

我们发现就可以直接定位出错原因了

所以core可以直接复现问题之后,直接定位到出错行

也就是说,先运行,在core-file,是事后调试

所以它就可以为这些最常出现的问题,有一个core功能去终止。

image-20240126181341219

所以这个就相当于Term + Core

为什么这个功能云服务器是关闭的呢?

因为core dump功能消耗的内存比较大。而我们的服务器一般一旦挂掉就会自动重启。计算机的速度是很快的。如果重启后又挂掉了。这样瞬间会冲击磁盘。磁盘被写满后,可能操作系统也会挂掉。

此时问题就严重了。所以一般都要禁掉这个功能的。

这篇关于【Linux】第三十六站:信号的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/655094

相关文章

linux生产者,消费者问题

pthread_cond_wait() :用于阻塞当前线程,等待别的线程使用pthread_cond_signal()或pthread_cond_broadcast来唤醒它。 pthread_cond_wait() 必须与pthread_mutex 配套使用。pthread_cond_wait()函数一进入wait状态就会自动release mutex。当其他线程通过pthread

Linux 安装、配置Tomcat 的HTTPS

Linux 安装 、配置Tomcat的HTTPS 安装Tomcat 这里选择的是 tomcat 10.X ,需要Java 11及更高版本 Binary Distributions ->Core->选择 tar.gz包 下载、上传到内网服务器 /opt 目录tar -xzf 解压将解压的根目录改名为 tomat-10 并移动到 /opt 下, 形成个人习惯的路径 /opt/tomcat-10

RedHat运维-Linux文本操作基础-AWK进阶

你不用整理,跟着敲一遍,有个印象,然后把它保存到本地,以后要用再去看,如果有了新东西,你自个再添加。这是我参考牛客上的shell编程专项题,只不过换成了问答的方式而已。不用背,就算是我自己亲自敲,我现在好多也记不住。 1. 输出nowcoder.txt文件第5行的内容 2. 输出nowcoder.txt文件第6行的内容 3. 输出nowcoder.txt文件第7行的内容 4. 输出nowcode

【Linux进阶】UNIX体系结构分解——操作系统,内核,shell

1.什么是操作系统? 从严格意义上说,可将操作系统定义为一种软件,它控制计算机硬件资源,提供程序运行环境。我们通常将这种软件称为内核(kerel),因为它相对较小,而且位于环境的核心。  从广义上说,操作系统包括了内核和一些其他软件,这些软件使得计算机能够发挥作用,并使计算机具有自己的特生。这里所说的其他软件包括系统实用程序(system utility)、应用程序、shell以及公用函数库等

【操作系统】信号Signal超详解|捕捉函数

🔥博客主页: 我要成为C++领域大神🎥系列专栏:【C++核心编程】 【计算机网络】 【Linux编程】 【操作系统】 ❤️感谢大家点赞👍收藏⭐评论✍️ 本博客致力于知识分享,与更多的人进行学习交流 ​ 如何触发信号 信号是Linux下的经典技术,一般操作系统利用信号杀死违规进程,典型进程干预手段,信号除了杀死进程外也可以挂起进程 kill -l 查看系统支持的信号

Windows/macOS/Linux 安装 Redis 和 Redis Desktop Manager 可视化工具

本文所有安装都在macOS High Sierra 10.13.4进行,Windows安装相对容易些,Linux安装与macOS类似,文中会做区分讲解 1. Redis安装 1.下载Redis https://redis.io/download 把下载的源码更名为redis-4.0.9-source,我喜欢跟maven、Tomcat放在一起,就放到/Users/zhan/Documents

Linux系统稳定性的奥秘:探究其背后的机制与哲学

在计算机操作系统的世界里,Linux以其卓越的稳定性和可靠性著称,成为服务器、嵌入式系统乃至个人电脑用户的首选。那么,是什么造就了Linux如此之高的稳定性呢?本文将深入解析Linux系统稳定性的几个关键因素,揭示其背后的技术哲学与实践。 1. 开源协作的力量Linux是一个开源项目,意味着任何人都可以查看、修改和贡献其源代码。这种开放性吸引了全球成千上万的开发者参与到内核的维护与优化中,形成了

Linux 下的Vim命令宝贝

vim 命令详解(转自:https://www.cnblogs.com/usergaojie/p/4583796.html) vi: Visual Interface 可视化接口 vim: VI iMproved VI增强版 全屏编辑器,模式化编辑器 vim模式: 编辑模式(命令模式)输入模式末行模式 模式转换: 编辑-->输入: i: 在当前光标所在字符的前面,转为输入模式

Linux和Mac分卷压缩

使用 zip 命令压缩文件 使用 zip 命令压缩文件,并结合 split 命令来分卷: zip - largefile | split -b 500k 举例: zip - ./tomcat.dmg |split -b 500k 上述命令将文件 largefile 压缩成 zip 包并分卷成不超过 500k 的文件,分解后文件名默认是 x* ,后缀为 2 位a-z 字母,如 aa、ab。

DDS信号的发生器(验证篇)——FPGA学习笔记8

前言:第一部分详细讲解DDS核心框图,还请读者深入阅读第一部分,以便理解DDS核心思想 三刷小梅哥视频总结! 小梅哥https://www.corecourse.com/lander 一、DDS简介         DDS(Direct Digital Synthesizer)即数字合成器,是一种新型的频率合成技术,具有低成本、低功耗、高分辨率、频率转换时间短、相位连续性好等优点,对数字信