当前位置: 首页 > news >正文

Linux之信号的产生,保存,捕捉

Linux之信号的产生,保存,捕捉处理

  • 一.信号的概念
    • 1.1概念
    • 1.2分类
  • 二.信号的产生
    • 2.1通过键盘产生的信号
    • 2.2系统调用接口产生的信号
    • 2.3硬件异常产生的信号
    • 2.4软件条件产生的信号
  • 三.信号的保存
  • 四.信号的捕捉
  • 五.信号的其他杂碎知识
    • 5.1可重入函数
    • 5.2volatile关键字
    • 5.3SIGCHLD信号

一.信号的概念

1.1概念

在我们日常生活中也有着信号的存在,简单一点的就像红绿灯,狼烟,下课铃声,喇叭等等都算是信号,那么在我们发现这些信号产生的时候我们通常会做出一些对应的行为比如红灯停,绿灯行,下课铃声一响就去厕所还有发现狼烟就说明有外敌入侵。这说明我们是可以知道这些信号产生的时候我们要做是什么的那么为什么可以识别出来是因为我们从小会被教育说红灯停绿灯行,在历史书上学到狼烟的作用。并且我们有感官来识别出来这些不同信号的产生,所以面对信号从我们日常生活的经验来说可以总结出两步:先识别出是什么信号再根据自己的记忆或本能来做出对应的事情。

同时我们也能从生活中的信号挖掘出来几点现象:

  1. 信号没有产生的时候我们都已经知道如何处理对应的信号了
  2. 我们不知道信号什么时候产生,并且信号的产生相对于我们现在做的工作是异步产生的。可能我们正在骑车,突然后方就有人按喇叭提醒我们。喇叭声音的出现相对于我们骑车的动作就是异步产生的。
  3. 对应产生的信号我们不一定要立刻去执行它,我们可以先记住这个信号然后等到合适的时机再去执行。

而将生活中的现象代入到我们Linux中同样可以兼容,所以在Linux中我们面对信号要有预备处理和保存的能力,而Linux中的我们指的就是进程。
那么在Linux中信号的概念就是信号是进程之间事件异步通知的一种方式。也属于进程间通信的一种,但是与之前的管道,共享内存消息队列的作用不同。

1.2分类

在Linux中我们可以使用kill -l命令来查看所有的信号
在这里插入图片描述

而在Linux中一共存在31+31=62个信号,这62个信号分为两类:普通信号和实时信号(今天对于信号的研究只针对普通信号)。其中1到31是普通信号,34到62是实时信号。

二.信号的产生

对于信号的产生大致可以分为四种方式:通过键盘产生的信号,系统调用接口产生的信号,硬件异常产生的信号以及软件条件产生的信号,我们来一个一个的介绍。

在介绍产生信号之前我们要先知道,当一个进程接收到一个信号之后它是如何判断它接收到的是哪个信号单论普通信号就已经有31个了。如何才能最简便的识别是哪个信号呢?
利用位图,我们只要创建一个32位的位图(多带一个0号,但是0号信号不存在),比特位的位置来判断信号的编号,比特位的内容来判断是否接收到信号。
在这里插入图片描述
但是在接收到了信号之后还不够,记得我们说的我们在接收到信号之前就已经知道如何处理信号了,那么对进程来说也要提前知道如何处理信号。所以每个进程的内部都会有一张函数指针数组,下标代表了信号的编号而内容就是对应信号的处理方法。所以对于进程来说有关信号的数据结构已经有了两个:信号位图和函数指针数组。

在了解了这两个知识后我们就可以更好理解以前说的发信号是什么意思,准确来说应该是写信号。操作系统不是向进程发送信号而是通过更改进程的信号位图来写入信号,无论我们使用什么方法来产生信号底层都是让操作系统通过更改进程的信号位图来写入信号。而这个现象的原因则是因为操作系统是进程的管理者!

2.1通过键盘产生的信号

在我们之前想要关闭一个正在执行的进程时我们可以通过同时按下ctrl+c的方式终止进程,那么在今天我就可以告诉大家这就是通过产生信号的方式来终止进程。但是准确的来说是ctrl+c是终止前台进程,那么什么是前台进程,ctrl+c又是产生了哪个信号来终止前台进程呢?

在Linux中我们想要将一个可执行文件变为进程的方式就是通过./可执行文件的方式,并且我们会发现通过这种方式生成的进程在运行时我们是无法再进行指令操作的。
在这里插入图片描述
这是因为我们通过./可执行文件产生的进程是前台进程,而如何判断是不是前台进程的方式就是能不能接收用户的输入,这个进程在运行的时候是接收了我们的输入但是它不像shell有相应的处理方法所以表现出来就是无视我们的指令。在提到了shell的时候我们会想到shell不也是个进程吗?再联想刚刚说的如何判断是不是前台进程的方法我们就可以知道我们平时在没有主动让某个可执行文件成为前台进程的时候shell一直就是我们的前台进程。那么在主动生成了新的前台进程后shell哪去了?

这就要提到我们相对于前台进程的后台进程了,想要产生后台进程我们可以通过./可执行文件 &的指令。同时我们可以使用jobs指令来查看后台进程。
在这里插入图片描述
我们发现在将刚刚的可执行文件运行为后台进程后我们还可以输入指令来操作,这就说明此时的前台进程还是shell。并且我们使用jobs来产生的后台进程列表中还为其标注了编号,那我们也可以从可以编号中得知后台进程是可以具有多个的但是前台进程只能有一个,所以是否可以将后台进程提到前台,将前台进程移到后台呢?
所以我们来介绍两个命令fg -number可以让后台进程提到前台,而用ctrl+z就可以让前台进程暂停,但是前台进程是无法暂停的,所以这个前台进程会被移到后台中,再使用bg+number就可以让暂停的进程在后台中继续运行。
在这里插入图片描述
在触发这些现象的时候我们发现只要一运行前台进程,shell就会自动变为后台进程而只要暂停了前台进程shell就又会自动变为前台进程。所以操作系统对shell的设定就是会根据情况来自动调整它为前台进程还是后台进程。

我们又学习到了ctrl+z可以暂停进程,这同样也是通过信号来完成的。但是我们有没有考虑过一个问题:为什么操作系统会知道键盘中有数据输入了呢?我们只是按下了ctrl+c或者ctrl+z,操作系统就自动识别其中的内容,这个知识牵涉到了计算机组成原理的知识,我来和大家大概的说一下。

在这里插入图片描述
那么在知道了中断号后我们就要使用程序来读取对应硬件输入的数据,所以在操作系统内还存在着一张中断向量表,它是一个函数指针数组其下标就是代表了各中断号而指针则是指向着不同硬件的读取方式。
在这里插入图片描述
所以操作系统是如何知道键盘输入数据了呢?就是根据中断来的。

2.2系统调用接口产生的信号

  1. kill接口
    在我们之前想要终止一个进程时不仅可以使用ctrl+c还可以利用kill命令来杀掉一个进程,而kill命令的底层就是一个系统调用接口kill函数
    在这里插入图片描述
    在知道了kill命令底层的逻辑后我们是否可以自己自定义一个kill命令呢?
    在这里插入图片描述
//process.cc
#include <iostream>
#include <stdio.h>
#include <sys/types.h>
#include <signal.h>int main(int argc, char* argv[])
{if(argc != 3){printf("\nUsge: kill -signumber pid");printf("\n");return 1;}int signumber = std::stoi((argv[1]+1));int pid = std::stoi((argv[2]));//printf("signumber:%d pid:%d\n",signumber,pid);kill(pid,signumber);    return 0;
}
//test.cc
#include <iostream>
#include <unistd.h>
#include <stdio.h>using namespace std;int main()
{while(1){printf("i am a process pid:%d\n",getpid());sleep(1);}return 0;
}
  1. raise接口
    raise函数可以让进程向自己发送信号
    在这里插入图片描述
    同时为了更加清晰的看见进程接收到了信号我们介绍一个函数signal(),这个函数的作用就是修改对应信号的处理方法。

注意:为了避免进程无法被任何信号暂停杀死终止,所以9号信号是无法通过signal修改处理方法的。

在这里插入图片描述
在这里插入图片描述

#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>using namespace std;void handle(int signo)
{cout << "i accept a sig:" << signo << endl;exit(0);
}
int main()
{signal(2,handle);int cnt = 0;while(1){cout << "i am a process  pid:" << getpid() << endl;sleep(1);if(++cnt == 5){raise(2);}}return 0;
}
  1. abort接口
    abort函数的作用就是像自己传递信号SIGABRT也就是6号信号,如果我们想要查看每个信号的作用是什么我们可以通过man 7 signal的指令。
    在这里插入图片描述
    在这里插入图片描述

我们发现SIGABRT的作用是Core也就是终止进程,所以我们使用了abort函数后进程就会被终止但是我们也可以使用signal来改变处理方法。
在这里插入图片描述

#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>using namespace std;void handle(int signo)
{cout << "i accept a sig:" << signo << endl;exit(0);
}int main()
{signal(6,handle);int cnt = 0;while(1){cout << "i am a process  pid:" << getpid() << endl;sleep(1);if(++cnt == 5){abort();}}return 0;
}

2.3硬件异常产生的信号

在C语言中有一个从现实生活中带来的异常叫做除零异常,在当时我们一直觉得这是语言层面的错误,是C语言让我们无法除零。而在今天我要告诉大家这不是语言层面的错误这是由硬件异常然后产生信号直接将进程终止了所以我们无法除零。其中的过程我用图来为大家解释。
在这里插入图片描述
而我们要知道操作系统是软硬件资源的管理者,所以操作系统必然会知道cpu发生了溢出异常所以操作系统就会处理这个异常而处理异常的方法就是利用kill命令来杀掉导致异常发生的进程。
在这里插入图片描述
在这里插入图片描述
Floating point exception是浮点数错误,这也是由信号产生的我们可以通过信号列表来找到其对应的信号编号,随后我们可以利用handle来修改处理方法从而来验证我们的说法。
在这里插入图片描述
那么如果我们修改处理方法不让使得发出信号但是不让这个进程退出呢?会发生什么?
在这里插入图片描述
我们会发现进程一直在接收信号,这是为什么呢?
我们要清楚发生除零异常的根本是什么,是因为我们进程中编写的代码所以如果我们不让这个进程退出,那么就会形成一个循环:在进行运行到了除零操作后cpu检测出了溢出所以让status中的溢出标志位置1随后被操作系统发现并将其解释为终止进程,但是我们修改了处理方法使得进程没有被终止仍在继续运行所以溢出标志位仍然为1然后再被操作系统发现,以此为循环。

那么可能会有人问:为什么操作系统不在kill操作后将溢出标志位置0呢?
首先我们要知道寄存器不等于寄存器的内容,寄存器是硬件而寄存器的内容是属于对应的进程的所以严格来说他是属于进程的上下文。那么在发生除零错误之后只要操作系统把进程终止了那么也就不想要管这个溢出标志位了因为当cpu在调度其他的进程时就可以直接让此进程所携带的上下文也包括寄存器内容将其覆盖了。所以操作系统不需要修改寄存器内容只需要让后面的进程的寄存器内容覆盖之前的内容即可这样溢出标记位自然就会置为0了。对于操作系统来说,杀掉进程是默认的一种处理问题的方法。

除了除零异常我们还可以举例比如以前学习指针时碰到的野指针异常,这个在当时我们知道是产生了硬件的异常但是不知道其具体的内容所以今天我们就结合之前的知识来深入理解一下。
在这里插入图片描述
在这里插入图片描述

我们首先要介绍一个在cpu中的小硬件mmu,它的功能是将虚拟地址转化为物理地址。在我们学习了虚拟地址空间后我们知道我们定义的变量的地址都是虚拟地址,而当我们想要调用这个变量的时候是cpu通过页表和mmu来在寄存器中进行虚拟物理地址的转化的,而野指针异常的产生就是cpu无法在页表中找到对应的物理地址从而产生错误。所以这个异常的产生也是在cpu内的那么在这就和上面的除零异常相同,操作系统会发现并利用信号将对应的进程杀死。

2.4软件条件产生的信号

因为操作系统是软硬件资源的管理者所以在了解了因为硬件异常产生的信号后我们现在来了解因为软件条件从而产生的信号。
因为软件条件产生的信号其实我们之前在学习管道的时候已经见过了,就是当读端关闭后操作系统会将写端杀死。而杀死的方式就是通过信号,我们在信号列表中也能发现。
在这里插入图片描述
为什么这个管道的例子算是软件条件产生的信号呢?大家听管道的名字可能潜意识里觉得它是有关硬件的但是在我们学习了管道后我们知道管道其实就是个文件所以它是属于软件资源的。当我们将读端关闭后操作系统发现你的写端还是往管道文件里写入数据它就会觉得你读端都关闭了都没人读取了你还往里面写入数据干啥。所以操作系统就会利用信号将写端也关闭了,但是严格来说关闭读端算是因为软件异常而产生的信号。

那么在今天我们来使用另外一个例子:闹钟。
闹钟就是一种软件条件它是操作系统利用时间戳来产生的一种倒计时。并且由于我们可以设置很多的闹钟所以操作系统同时也需要管理闹钟,但是由于闹钟是具有顺序的即按照触发时间的顺序来排列闹钟所以闹钟的管理通常是使用我们之前在C++中学习到的一个数据结构:大小堆。
在Linux中存在一个闹钟函数alarm
在这里插入图片描述
那么我们来利用闹钟函数和signal函数来完成定时触发某些行为的操作。
在这里插入图片描述

三.信号的保存

在了解了信号的产生后我们现在要来学习信号是如何进行保存的,在这之前我们需要了解三个新名词:信号递达,信号未决,信号阻塞。
信号递达(Delivery):实际执行信号的处理动作。
信号未决(Penting):信号从产生到递达之间的状态。
信号阻塞 (Block):进程可以选择阻塞某个信号。
在了解了概念后我们来用可以理解的话来分别阐述一下这三个名称:
信号递达就是指对信号的处理包括信号的忽略,信号的默认操作以及我们通过signal函数产生的对信号的自定义操作。
在这里插入图片描述

注意:信号的忽略也是对于信号的一种处理方式,处理方式就是忽略,就好像你在路上你发现了你不喜欢的人然后你就心里说你要装看不见他也就是忽略他。忽略的前提是你已经发现他了然后对于发现他之后的处理方式是忽略他。

信号未决就是当操作系统正打算在进程的信号位图中修改位图的时候。
信号阻塞就是让信号一直处于在未决之后,递达之前的状态直到取消对信号的阻塞。

那么进程是如何保存信号的呢?
在学习了信号的递达,未决和阻塞后我们知道信号的保存分为三步,首先是判断信号是否未决然后是阻塞最后才是递达。所以根据先描述再组织的原理我们要使用数据结构来分别保存这三种状态那么使用什么数据结构呢?
还是位图和函数指针数组,我们只需要创建两个位图用代表未决的信号以及阻塞了的信号然后对于处理方法我们也只要使用一个函数指针数组来存储各个信号的处理方法即可。
在这里插入图片描述
我们不能光知道是如何保存信号的我们还要学习是如何对block表和pending表进行操作即信号集操作函数。
在这里插入图片描述
在了解信号集函数之前我们先介绍一个变量sigset_t,这个变量就是信号集也就是一个位图其可以代表信号的未决状况以及阻塞状况。

  1. sigemptyset
    在这里插入图片描述
  2. sigfillset
    在这里插入图片描述
  3. sigaddset/sigdelset
    在这里插入图片描述
  4. sigigmember
    在这里插入图片描述

注意:
1.在使用信号集变量前必须使用sigemptyset或sigfillset函数来进行初始化从而让信号集变量处于一种稳定的状态。之后才能使用sigaddset和sigdelset来增加或删除信号。
2.这五个函数的返回值都是成功为0,失败为-1。

  1. sigprocmask
    在这里插入图片描述
  2. sigpending
    在这里插入图片描述
    我们在了解了信号集操作函数后我们可以做一些测试代码来使用一下这些函数。
    在这里插入图片描述
#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>using namespace std;void PrintfPending(sigset_t* set)
{int cnt = 31;for(cnt;cnt > 0;cnt--){if(sigismember(set,cnt)){cout << "1";}        else{cout << "0";}}cout << endl;
}void handle(int signo)
{cout << "i accept a sig:" << signo << endl;//打印pending表sigset_t pending;sigemptyset(&pending);while(1){sigpending(&pending);PrintfPending(&pending);sleep(1);}exit(0);
}//信号集
int main()
{signal(2,handle);//创建信号集变量 sigset_t mask,omask;//初始化信号集sigemptyset(&mask);sigemptyset(&omask);//增添信号屏蔽sigaddset(&mask,19);//向block表中增加信号屏蔽sigprocmask(SIG_BLOCK,&mask,&omask);cout << "i am a process pid:" << getpid() << endl;while(1){sleep(1);cout << "running..." << endl;}return 0;
}

注意:
在检测到某个信号要被递达时,会在执行处理方法前就将信号的未决标志位修改了。
操作系统不允许一个信号在短时间内被重复获取,所以会在接收到某个信号后在一定时间内屏蔽掉这个信号直到允许再次获取这个信号。

四.信号的捕捉

我们从生活中提取的信号的几个问题如今只剩下了在保存了信号之后要在合适的时间处理它,那么什么才算是合适的时间呢?

这个合适的时间就是当进程从内核态返回到用户态的时候再进行信号的检测与处理。
那么内核态和用户态分别又是什么意思呢?我先把概念告诉大家之后我会用图来详细描述捕捉的过程其中也会包含对内核态和用户态的讲解。
内核态:是一种操作系统的工作状态,可以访问大多数的系统资源。
用户态:是一种受控的工作状态,只能访问一部分的系统资源。
在了解了概念后简单来说内核态就是处于一种更高的权限可以访问更多的资源,而用户态是受控的只能访问一些让你访问的资源。而这个访问资源的差异我们可以在虚拟地址空间中也能看出来。

在我们学习虚拟地址空间的时候我们给整个虚拟地址空间分为两大块,一块是0到3GB的用户空间,一块是3到4GB的内核空间,我们对于虚拟地址空间的学习一直都停留在用户空间上没有涉及内核空间这是因为内核空间只有在内核态的时候才能访问而我们用户平时是接触不到内核态的。那么这1GB的内核空间里面存放了什么呢?存放的是操作系统的代码,数据和数据结构,那么这也就可以解释了为什么很多的函数是系统调用接口的封装但是我们仍然可以使用。
在这里插入图片描述
但是我们要知道想要访问内核空间必须是内核态才行那么进程想要访问系统调用接口也就必须转为内核态这又是如何做到的呢?
在这里插入图片描述
那么在了解了我们内核空间以及进程是如何变换状态的之后我们就来理解信号的捕捉的全过程吧,一样上图。
在这里插入图片描述
我们要在这个过程中注意几个问题:

  1. 为什么访问自定义方法的时候需要转换到用户态
    这是因为内核态可以访问的资源更多如果有用户在自定义方法中编写一些只有内核态才能修改的数据并且修改后还是对操作系统不利的,那么操作系统就会变得不稳定。这也验证了我们之前说操作系统不相信任何用户就像银行那样的说法。
  2. 在整个信号的捕捉的过程中最多一共会经历四次转换状态:调用系统接口,执行自定义方法,通过sigreturn,输出返回值。所以整个过程我们可以进行简化。
    在这里插入图片描述

五.信号的其他杂碎知识

在了解了信号的概念,信号的产生,保存和捕捉后我们基本就了解了信号的前世今生,现在我们来了解一些和信号有关的一些其他的知识。

5.1可重入函数

大家可能没有听过可重入函数的概念,我们先用一个例子来为大家讲解一下可重入函数的意思
在这里插入图片描述
只要我们复盘这个情况我们就会发现一个问题,main函数和递达时都需要头插一个结点而且main函数的头插还没做完时就插入了信号递达的头插所以最后只会有一个结点头插成功另外一个结点则头插失败。
在这里插入图片描述
像上面这种一个函数被不同的执行流重复调用,可能在一个函数还没返回的时候就被再次进入再次运行的情况就被叫做这个函数被重入了即重复进入。如果像上面的头插导致一次调用的函数调用成功另外一次调用的函数失败那么这个函数就被叫做不可重入函数,如果两个或多次调用互相不影响都成功了则将这个函数叫做可重入函数。

要注意可重入函数和不同重入函数是没有好坏之分的不能说可重入就是好的不可重入就是坏的,能否重入只是一个函数的性质而已。那么想要判断一个函数是否是可重入函数的方法就是观察这个函数是否使用了全局变量,一般使用了全局变量的函数都是不可重入函数。

5.2volatile关键字

volatile关键字可能大家在学习C语言的时候碰到过当时只告诉我们这个关键字的作用是当要求使用 volatile声明的变量的值的时候,系统总是重新从它所在的内存读取数据,即使它前面的指令刚刚从该处读取过数据。而且读取的数据立刻被保存。

现在我们从信号的角度上去重新理解一下volatile。
在这里插入图片描述

但是这个是处于Debug模式下运行,我们在学习C语言的时候提到过当我们在使用Release模式下运行的时候编译器会对我们的代码进行优化来实现节省时间或空间的作用,但是当时我们就说到了这个优化有利有弊如果我们的代码很完美那么优化只会让其锦上添花如果我们的代码写的比较粗糙那么优化可能就会让其火上浇油甚至导致一些错误,而这次的代码就是这种效果。
如果我们在linux中使用Release模式优化代码只需要在使用编译器后加上-O2即可。
在这里插入图片描述
而这是如何造成的呢?
在我们没有使用优化时当我们运行到while循环的时候cpu的寄存器会一直读取判断内存中flag的值所以当我们在发出信号改变了flag的值的时候寄存器就会第一时间发现并且通过判断终止掉循环从而导致代码可以继续往下走。
但是当我们使用Release模式对代码进行优化了之后,cpu会直接将flag的值存在寄存器中所以当进行循环判断的时候cpu一直读取的都是寄存器中的值,所以当我们改变内存中flag的值后循环并没有停止因为那时候的cpu读取的根本不是内存中flag的值。

而我们volatile关键字的作用就是让cpu每次读取变量的时候都是从内存中读取即使是优化模式。简单来说volatile的作用就是保存内存的可见性。
在这里插入图片描述

5.3SIGCHLD信号

在我们学习fork的时候我们创建了子进程后我们只知道子进程在退出的时候需要父进程来回收但是其实子进程在退出不是什么都没做,反而它在退出的时候会向父进程发送一个信号SIGCHLD即17号信号
在这里插入图片描述
我们可以用代码来进行证明
在这里插入图片描述

相关文章:

Linux之信号的产生,保存,捕捉

Linux之信号的产生&#xff0c;保存&#xff0c;捕捉处理 一.信号的概念1.1概念1.2分类 二.信号的产生2.1通过键盘产生的信号2.2系统调用接口产生的信号2.3硬件异常产生的信号2.4软件条件产生的信号 三.信号的保存四.信号的捕捉五.信号的其他杂碎知识5.1可重入函数5.2volatile关…...

基于AutoEncode自编码器的端到端无线通信系统matlab误码率仿真

目录 1.算法仿真效果 2.算法涉及理论知识概要 3.MATLAB核心程序 4.完整算法代码文件获得 1.算法仿真效果 matlab2022a仿真结果如下&#xff08;完整代码运行后无水印&#xff09;&#xff1a; 仿真操作步骤可参考程序配套的操作视频。 2.算法涉及理论知识概要 自编码器是…...

泛化调用 :在没有接口的情况下进行RPC调用

什么是泛化调用&#xff1f; 在RPC调用的过程中&#xff0c;调用端向服务端发起请求&#xff0c;首先要通过动态代理&#xff0c;动态代理可以屏蔽RPC处理流程&#xff0c;使得发起远程调用就像调用本地一样。 RPC调用本质&#xff1a;调用端向服务端发送一条请求消息&#x…...

2025年人工智能,自动化与机械工程国际学术会议(AIAME2025)

早鸟通道开启&#xff1a; 2025年人工智能&#xff0c;自动化与机械工程国际学术会议&#xff08;AIAME2025&#xff09; 2025 International Conference on Artificial Intelligence, Automation, and Mechanical Engineering 【重要日期】 早鸟征稿截止日期&#xff1a;…...

docker compose 快速搭建Nacos单节点测试环境(mysql 版)

〓 参考&#xff1a; https://nacos.io/docs/latest/quickstart/quick-start-docker/?sourcewuyi https://github.com/nacos-group/nacos-docker https://nacos.io/docs/latest/manual/admin/deployment/deployment-standalone/?sourcewuyi https://nacos.io/docs/latest/man…...

数字3D虚拟展厅成熟运用于旅游业

在数字空间展览会与VR3D虚拟企业展厅设计的兴起中&#xff0c;我们迎来了互联网、物联网与3D技术融合的大时代。这些企业虚拟展厅主要依托互联网作为传播媒介&#xff0c;利用图片、文字和Flash动画等形式&#xff0c;生动展现企业的核心产品。作为一种新型的网络信息技术展厅&…...

模数转换芯片AD9215

AD9215 是 Analog Devices 公司推出的一款高性能、低功耗、单通道 10 位模数转换器(ADC)。它具有采样速率高达 65 MSPS 或 105 MSPS(不同型号),并广泛应用于通信、成像和仪器仪表等领域。 AD9215 的关键特性 分辨率: 10 位,适合高精度应用。采样速率: 两种型号: AD921…...

MongoDB注入攻击测试与防御技术深度解析

MongoDB注入攻击测试与防御技术深度解析 随着NoSQL数据库的兴起&#xff0c;MongoDB作为其中的佼佼者&#xff0c;因其灵活的数据模型和强大的查询能力&#xff0c;受到了众多开发者的青睐。然而&#xff0c;与任何技术一样&#xff0c;MongoDB也面临着安全威胁&#xff0c;其…...

总结贴:Servlet过滤器、MVC拦截器

一:Servlet过滤器 1.1解析 Filter 即为过滤&#xff0c;用于请求到达Servlet之前(Request),以及再Servlet方法执行完之后返回客户端进行后处理(HttpServletResponse)。简单说就是对请求进行预处理&#xff0c;对响应进行后处理 在请求到达Servlet之前,可以经过多个Filt…...

鸿蒙开发-在ArkTS中制作音乐播放器

音频播放功能实现 导入音频播放相关模块 首先需要从ohos.multimedia.audio模块中导入必要的类和接口用于音频播放。例如&#xff1a; import audio from ohos.multimedia.audio;创建音频播放器实例并设置播放源 可以通过audio.createAudioPlayer()方法创建一个音频播放器实…...

mapstruct DTO转换使用

定义一个基础接口 package com.example.mapstruct;import org.mapstruct.Named;import java.time.LocalDate; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.ZonedDateTime; import java.util.Date; import java.util.List;/*** Author zmn Dat…...

C++内存对齐

一、内存对齐的定义 内存对齐是一种计算机内存管理策略。在这种策略下&#xff0c;数据存储的内存地址必须是数据类型大小&#xff08;或者是某个特定对齐模数&#xff09;的整数倍。 例如&#xff0c;在一个 32 位系统中&#xff0c;如果一个int类型&#xff08;通常占用 4 …...

关于node全栈项目打包发布linux项目问题总集

1.用pm2部署nest 说明&#xff1a;如果一开始将nest直接打包放到linux服务器上用pm2执行则会报错&#xff0c;这是因为tsconfig.build.tsbuildinfo文件的路径以及相关依赖问题。 报错会为&#xff1a;什么东西找不到.... 所以建议以下为步骤一步一步配置 将整个nest添加压缩包直…...

40 基于单片机的温湿度检测判断系统

目录 一、主要功能 二、硬件资源 三、程序编程 四、实现现象 一、主要功能 基于STC89C52单片机&#xff0c;采用dht11温湿度传感器检测温湿度&#xff0c; 通过lcd1602显示屏各个参数&#xff0c;四个按键分别可以增加温湿度的阈值&#xff0c; 如果超过阈值&#xff0c;则…...

Vue 原理详解

Vue 原理详解 Vue.js 是一个渐进式框架&#xff0c;它通过数据驱动视图更新和响应式编程使得前端开发变得更加简单高效。在 Vue 的内部实现中&#xff0c;编译过程和响应式机制是两个至关重要的组成部分。本文将详细介绍 Vue.js 的编译器、响应式系统和运行时的工作原理&#…...

w064基于springboot的高校学科竞赛平台

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以查看文章末尾⬇️联系方式获取&#xff0c;记得注明来意哦~&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0…...

vue实现弹窗输入验证码

实现思路&#xff1a;前端输入完账号和密码&#xff0c;点击登录按钮的时候&#xff0c;弹出一个输入验证码的窗口&#xff0c;后端把验证码图片通过base64的字符传给前端&#xff0c;前端把字符当成图片展示出来。输入完验证码&#xff0c;点击确认进行登录&#xff0c;把验证…...

maven,java相关调试等

maven 增加调试信息的命令&#xff1a; mvn clean compile -Xmvn -X clean installmvn -e exec:execmodule jdk.compiler does not “opens com.sun.tools.java c.processing” 报错是因为用了JDK17&#xff0c;而老版本的1.18.4不支持。将lombok升级到1.18.32问题解决。 报错…...

ARP欺骗-断网攻击

ARP协议 arp协议(地址解析) &#xff0c;在局域网中传输的是帧&#xff0c;帧里面有目标主机的MAC地址&#xff0c;其中一台电脑和另一台电脑需要知道对面的ip地址所对应的MAC地址 ARP欺骗的原理 把自己的MAC地址伪造成网段来欺骗其他用户 实验环境 kali:192.168.21.128 win…...

鬼谷子的捭阖之道

捭&#xff08;bai&#xff09;是打开&#xff0c;开口说的意思&#xff0c;代表阴阳中的阳面 阖&#xff08;he&#xff09;是关闭&#xff0c;是闭嘴、观察&#xff0c;代表阴阳中的阴面 捭阖就是通过话术来试探对方的实情&#xff0c;用谋略让对方信服&#xff0c;从而推动…...

mysql之找回忘记的root密码

mysql之找回忘记的root密码 1.方法1&#xff0c;init-file重置密码2.方法2&#xff0c;–skip-grant-tables重置密码 1.方法1&#xff0c;init-file重置密码 使用init-file参数来对密码进行重新设置 1.停止mysql服务进程 首先将mysql的服务停用掉&#xff1b; 输入命令&#x…...

IDEA中Maven相关使用

一、Maven 的配置文件与本地仓库 Maven 是一种基于配置的工具&#xff0c;主要通过 配置文件 和 本地仓库 管理项目构建与依赖。 1. Maven 配置文件的层级 Maven 的配置文件分为两个层级&#xff1a;全局配置 和 用户配置。 &#xff08;1&#xff09;全局配置 位置&#…...

C语言基础数据类型

C语言------基础数据类型 思考、实践、总结、交流&#xff0c;八字真言是学习任何一门知识的内功&#xff0c;尤其是在很方便用鸡皮提的时代中&#xff0c;独立思考是很重要的。 一个 C 语言工程由多个.c(源码文件) .h&#xff08;头文件&#xff09;组成。.c 文件是实现逻辑的…...

excel中字符数字转换为数值类型:NUMBERVALUE()函数

excel中字符数字转换为数值类型&#xff1a;NUMBERVALUE()函数 例子&#xff1a; 假如这个文档被设置为文档类型&#xff1a;可以通过这个函数进行转换 有时候这个函数不起作用&#xff1a;可以试试对目标列的第一个字符数字进行设置单元单元格-设置为数值型 然后对第一个字…...

【LeetCode刷题之路】283:移动零的普通解法与优化解法(含动图演示)

LeetCode刷题记录 &#x1f310; 我的博客主页&#xff1a;iiiiiankor&#x1f3af; 如果你觉得我的内容对你有帮助&#xff0c;不妨点个赞&#x1f44d;、留个评论✍&#xff0c;或者收藏⭐&#xff0c;让我们一起进步&#xff01;&#x1f4dd; 专栏系列&#xff1a;LeetCode…...

AtomicIntegerFieldUpdater能否降低内存

1. 代码如下&#xff1a; import java.util.LinkedList; import java.util.List; import java.util.concurrent.atomic.AtomicInteger;public class AtomicIntegerTest {final AtomicInteger startPosition new AtomicInteger(0);final AtomicInteger wrotePosition new Atom…...

一个实用的 Maven localRepository 工具

目录 1 现状2 当前解决3 更好的解决3.1 下载 Maven localRepository 工具包3.2 上传本地 localRepository 包3.3 清理 localRepository 中指定后缀的文件 1 现状 在使用 Maven 时&#xff0c;我们可能会经常与本地仓库和私服仓库打交道。 例如对于本地仓库&#xff0c;因为某…...

【算法day4】链表:应用拓展与快慢指针

题目引用 两两交换链表节点删除链表的倒数第n个节点链表相交环形链表 1.两两交换链表节点 给你一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题&#xff08;即&#xff0c;只能进行节点交换&am…...

拉格朗日乘子法和罚函数法

求目标函数 f ( x , y ) x 2 y 2 f(x, y) x^2 y^2 f(x,y)x2y2 的极值 约束条件为&#xff1a; g ( x , y ) x y − 1 0 g(x, y) x y - 1 0 g(x,y)xy−10 直接求解 消去变量由约束条件 x y − 1 0 x y - 1 0 xy−10&#xff0c;可得 y 1 − x y 1 - x y1−…...

关于创建新登录用户使用SQL Server验证登录时报错全过程解决方法(错误: 18456,不包含网络连接,此教程为本地单机连接)

想必大家在创建新登录用户时会遇到一些问题例如: 但是账号密码都已经反复确认过没错了 之前我在很多博文看到的解决方法都不能正确解决这个问题&#xff0c;接下来我就来解决下这个问题 1.首先创建一个新的登录用户 下面配置是这样子的&#xff1a; 登录名test login 密码12…...

前端通用Axios 请求拦截配置

前端通用Axios 请求拦截配置 配置 目录在request或者plugins都可以&#xff0c;看自己习惯 文件名ReqAxios.js/ReqAxios.ts import axios from "axios"; //消息提示 import { message } from "ant-design-vue"; import Vue from vueconst reqAxios ax…...

docker搭建socks5代理

准备工作 VPS安全组/策略放行相应端口如启用了防火墙&#xff0c;放行相应端口 实际操作 我们选用“历史悠久”的Dante socks5 代理服务器&#xff0c;轻量、稳定。Github也有对dante进行进一步精简的镜像&#xff0c;更为适宜。github项目地址如下&#xff1a; https://gi…...

抓包之wireshark基础用法介绍

写在前面 wireshark作为最优秀的抓包工具&#xff0c;有必要详细的看下其基本用法&#xff0c;所以本文就一起来做这件事吧&#xff01; 1&#xff1a;初步介绍 打开wireshark首先会进入如下的界面&#xff1a; 想要开始抓包&#xff0c;需要进行如下操作&#xff1a; 接着…...

JVM的生命周期

目录 引言1. 虚拟机的启动1.1 引导类加载器1.2 初始化步骤 2. 虚拟机的执行2.1 主要任务2.2 运行时数据区2.3 垃圾收集2.3.1 标记-清除算法2.3.2 复制算法2.3.3 标记-整理算法2.3.4 分代收集 3. 虚拟机的退出3.1 正常退出3.2 异常退出3.3 其他退出方式3.4 关闭钩子 4. 结论 引言…...

【第三讲】Spring Boot 3.4.0 新特性详解:增强的配置属性支持

Spring Boot 3.4.0 版本在配置属性的支持上进行了显著增强&#xff0c;使得开发者能够更灵活地管理和使用应用程序的配置。新的特性包括对配置属性的改进、类型安全增强、以及对环境变量的更好支持。这些改进旨在提升开发效率和代码可读性&#xff0c;同时简化配置过程。本文将…...

搭建环境-PHP简介及环境搭建教程

搭建环境-PHP简介及环境搭建教程 前言 在现代Web开发中,PHP是一种广泛使用的服务器端脚本语言,它以简洁、高效和跨平台的特性受到开发者的青睐。无论是小型网站还是大型企业应用,PHP都能提供强大的支持。本文将为您详细介绍PHP的基本概念、特点,以及如何搭建PHP开发环境。…...

linux模拟HID USB设备及wireshark USB抓包配置

文章目录 1. 内核配置2. 设备配置附 wireshark USB抓包配置 linux下模拟USB HID设备的简单记录&#xff0c;其他USB设备类似。 1. 内核配置 内核启用USB Gadget&#xff0c;使用fs配置usb device信息。 Device Drivers ---> [*] USB support ---><*> USB …...

微前端架构 qiankun

背景&#xff1a;随着业务功能的扩展&#xff0c;原有开发模式已无法满足需求。上线后出现问题时&#xff0c;排查过程变得异常复杂&#xff0c;新开发人员也难以迅速理解现有代码。同时&#xff0c;系统间界面风格和交互差异较大&#xff0c;导致跨系统办理业务时工作量增加。…...

RAT:融合RAG和CoT的高效多步推理任务策略

今天分享的是由北京大学、加州大学洛杉矶分校和北京通用人工智能研究院合作发表的一篇文章 论文题目:RAT: Retrieval Augmented Thoughts Elicit Context-Aware Reasoning in Long-Horizon Generation 论文链接:https://arxiv.org/pdf/2403.05313 代码地址:https://githu…...

C++之虚基类

虚基类&#xff08;Virtual Base Class&#xff09;是 C 中的一个特性&#xff0c;用于解决菱形继承问题&#xff0c;避免因为多重继承而导致的重复继承和冗余问题。 菱形继承问题 假设有如下的类结构&#xff1a; 一个基类 Base。两个类 Derived1 和 Derived2 继承自 Base。…...

大小写转换

描述 将下面的字符串中的大小写进行转换。 输入描述 输入一行仅包含字母的字符串(字符串长度 ≤100)。 输出描述 将其中的大写转换为小写&#xff0c;小写转换为大写。 abcD ABCd #include<iostream> #include<string> using namespace std; int main() { …...

Flink 热存储维表 使用 Guava Cache 减轻访问压力

目录 背景 Guava Cache 简介 实现方案 1. 项目依赖 2. Guava Cache 集成到 Flink (1) 定义 Cache (2) 使用 Cache 优化维表查询 3. 应用运行效果 (1) 维表查询逻辑优化 (2) 减少存储压力 Guava Cache 配置优化 总结 背景 在实时计算场景中&#xff0c;Flink 应用中…...

09.ES13 10.ES14

9.1、class扩展 9.1.1、类成员声明 在ES13之前&#xff0c;我们只能在构造函数里面声明类的成员&#xff0c;而不能像其他大多数语言一样在类的最外层作用域里面声明成员。不过ES13出来之后&#xff0c;这都不算什么事儿了。现在我们终于可以突破这个限制&#xff0c;写下面这…...

Day 30 贪心算法 part04

今天的三道题目,都算是 重叠区间 问题,大家可以好好感受一下。 都属于那种看起来好复杂,但一看贪心解法,惊呼:这么巧妙! 这种题还是属于那种,做过了也就会了,没做过就很难想出来。 不过大家把如下三题做了之后, 重叠区间 基本上差不多了 452. 用最少数量的箭引爆气球…...

ProtonBase 教育行业解决方案

01/方案概述 当前&#xff0c;大数据、云计算等技术正加速教育行业的数字化转型&#xff0c;教学模式从线下转向线上&#xff0c;传统教育企业向具有互联网性质的新型教育企业转变。在此背景下&#xff0c;教育企业亟需探索多源数据的融合扩展&#xff0c;以应对复杂的业务场景…...

mimic插件使用

最近搞机械臂的末端夹具&#xff0c;本来想用吸盘的插件的&#xff0c;不知道为什么吸盘吸不起来可乐瓶&#xff0c;后面就换成夹爪了。 因为原厂的urdf文件中提供夹爪是用mimic标签控制剩下的5个joint关节的&#xff0c;网上参考的资料太少了&#xff0c;也是废了好多力 气&am…...

Docker+Jenkinsg+Springboot流水式构建-实用篇

最近无聊想自己玩一玩devpos&#xff0c;方便以后接私活&#xff0c;或者学习&#xff0c;O(∩_∩)O&#xff0c; 以后直接安装这篇文档&#xff0c;傻瓜式安装&#xff0c;哈哈 废话不多说&#xff0c;直接进入实战&#xff0c;完成简单的搭建 1.初始化CentOS环境 1.1 关闭防…...

华为小米苹果三星移动设备访问windows共享文件夹windows11

如果移动设备和windows电脑都在同一个局域网内&#xff0c;可以用移动设备访问windows11的共享文件夹 1、设置共享文件夹 2、添加everyone用户即可 3、查看ip地址 4、在华为手机上点击文件管理&#xff0c;里面有个网上邻居 5、正常情况下&#xff0c;华为手机会扫描到同一局域…...

程序执行堆栈执行模拟

所有的文件都是在硬盘&#xff08;磁盘&#xff09;上&#xff0c;调用时先调用javac指令的jdk编译成.class然后被java指令的jre送到内存中&#xff0c;java在内存中有自己的一片区域叫JVM&#xff0c;编译进来的文件首先进入方法区。 staitc的属性就是在进入内存的时候开辟了一…...

【AIGC】ChatGPT提示词Prompt助力高效文献处理、公文撰写、会议纪要与视频总结

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: AIGC | 提示词Prompt应用实例 文章目录 &#x1f4af;前言&#x1f4af;高效英文文献阅读提示词使用方法 &#x1f4af;高效公文写作提示词使用方法 &#x1f4af;高效会议纪要提示词使用方法 &#x1f4af;高效视频内…...