[内功修炼]函数栈帧的创建与销毁
文章目录
- 1:什么是函数栈帧
- 2:理解函数栈帧能解决什么问题呢
- 3:函数栈帧的创建与销毁的解析
- 3.1:什么是栈
- 3.2:认识相关寄存器与汇编指令
- 相关寄存器
- 相关汇编指令
- 3.3 解析函数栈帧的创建和销毁
- 3.3.1 预备知识
- 3.3.2 详细解析
- 一:调用main函数,为main函数开辟函数栈帧
- First:
- push前
- push后
- Second:
- mov前
- mov后
- Third:
- sub前
- sub后
- Fourth
- 三次push前
- 三次push后
- Fifth:
- lea前
- lea后
- Sixth
- mov前:
- mov后:
- seventh
- 拷贝前
- 拷贝后
- eighth
- mov前
- mov后
- ninth
- mov前
- mov后
- tenth
- eleventh
- call前
- call后
- twelfth
- thirteenth
- push前
- push后
- fourteenth
- mov前
- mov后
- fifteenth
- sub前
- sub后
- sixteenth
- 三次push前
- 三次push后
- Seventeenth
- lea前
- lea后
- eighteenth
- mov前
- mov后
- nineteenth
- 拷贝前
- 拷贝后
- twentieth
- mov前
- mov后
- twenty-first
- Add前
- Add后
- Twenty-Second
- mov前
- mov后
- Twenty-Third
- Twenty-fourth
- 三次pop前
- 三次pop后
- Twenty-fifth
- Twenty-sixth
- pop前
- pop后
- Twenty-Seventh
- Twenty-eighth
- Add前
- Add后
- Twenty-ninth
- mov前
- mov后
- Thirtieth
- 4.:前期问题解答
- 4.1:局部变量是如何创建的
- 4.2:为什么局部变量的值不初始化时是随机值?
- 4.3:函数是怎么传参的?传参的顺序是怎么样的
- 4.4:形参和实参是什么关系?
- 4.5: 函数是怎么做的以及函数调用结束后怎么返回的?
嘿嘿,家人们,今天呢咱们来详细讲解函数栈帧的创建与销毁,好啦,废话不多讲,开干!
1:什么是函数栈帧
我们在写C语言代码的时候,经常会把一个独立的功能封装在一个函数中,因此,C程序是以函数为基本单位的,那函数是如何调用的呢?函数的返回值又是如何带回呢?函数的参数又是如何传递的呢?这些都与函数的栈帧有关系。
函数栈帧就是用来函数在调用过程中在程序的调用栈所开辟的空间,这些空间是用来存放:
(1):函数参数和返回值
(2):临时变量(包括函数的非静态区的局部变量以及编译器自动产生的其他临时变量)
(3):保存上下文信息 (包括在函数调用前后需要保持不变的寄存器)。
2:理解函数栈帧能解决什么问题呢
只要理解了函数栈帧,那么如下问题就能很好地理解了,并且更方便我们日后的学习!
(1):局部变量是如何创建的?
(2):为什么局部变量不初始化内容就被赋予随机值?
(3):函数调用时参数是如何传递的?传参的顺序是怎么样的?
(4):函数的形参与实参分别是怎么样实例化的?
(5):函数的返回值是如何带回的?
那么就让我们一起走进函数栈帧的创建与销毁的过程中。
3:函数栈帧的创建与销毁的解析
在解析函数栈帧的创建与销毁之前,首先呢得了解一些预备知识,这样子方便后续的理解。
3.1:什么是栈
栈是现代计算机程序里最为重要的概念之一,几乎每一个程序都使用了栈,没有栈就没有函数,没有局部变量,同时也就没有我们如今看到的所有的计算机语言。
在经典的计算机科学中,栈被定义为一种特殊的容器,用户可以将数据压入栈中(入栈,push),也可以将已经压入栈中的数据弹出(出栈,pop),但是栈这个容器必须遵守一条规则:先进后出(先入栈的数据后出栈).就好比叠成一叠的书,先叠上去的书在最下面,因此要最后才能取出。
在计算机系统中,栈则是一个具有以上属性的动态内存区域。程序可以将数据压入栈中,也可以将数据从栈顶弹出。压栈操作使得栈增大,而弹出操作使得栈减小。
在经典的操作系统中,栈总是向下增长(由高地址向低地址)的。
3.2:认识相关寄存器与汇编指令
相关寄存器
eax:通用寄存器,保留临时数据,常用于返回值。
ebx:通用寄存器,保留临时数据。
ebp:栈底寄存器。
esp:栈顶寄存器。
eip:指令寄存器,保存当前指令的下一条指令的地址
相关汇编指令
mov:数据转移指令。
push:数据入栈,同时esp栈顶寄存器也要发生改变。
pop:数据弹出至指定位置,同时esp栈顶寄存器也要发生改变。
sub:减法命令;
add:加法命令;
call:函数调用, 1. 压入返回地址 2.转入目标函数
jump:通过修改eip,转入目标函数,进行调用。
ret:恢复返回地址,压入eip,类似pop eip命令。
3.3 解析函数栈帧的创建和销毁
3.3.1 预备知识
了解了上面的相关知识后,我们还需要达成一些预备知识才能有效地帮助我们去理解,函数栈帧的创建与销毁。
1.:每一次函数调用,都要为本次函数调用开辟空间,就是函数栈帧的空间。
2.:这块空间的维护是使用了2个寄存器:esp和ebp,ebp记录的是栈底的地址,被称为栈顶指针,esp记录的栈底的地址,被称为栈底指针。
3.函数栈帧的创建和销毁过程,在不同的编译器上实现的方法是大同小异,博主在这里使用的是VS2019.建议家人们使用VS2013或者更低版本的编译器,不要使用更高的编译器,越高级的编译器,环境虽然稳定,但不容易进行观察和学习,不同版本的编译器在观察函数栈帧的创建与销毁是有些差异的。
3.3.2 详细解析
了解上面的预备知识后,博主将通过以下这段代码来具体解析函数栈帧的创建和销毁
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int Add(int a,int b)
{int value = a + b;return value;
}int main()
{int a = 10;int b = 20;int result = Add(a, b);printf("%d", result);return 0;
}
一:调用main函数,为main函数开辟函数栈帧
我们通过F10进行调试,然后进入窗口,点击里面的调用堆栈,我们可以清楚地看到,此时main函数被调用了,但是它是被谁调用的呢?这里博主引用了下vs2013的环境下main函数的调用情况。因为在vs2019的环境是看不到main函数时被哪个函数调用的.
从这张图片中,我们可以得知,main函数是由一个叫** _tmainCRTStartup函数 所调用,而 _tmainCRTStartup是由mainCRTStartup所调用的**,在之前的预备知识我们讲到过,每一次调用函数都要为函数的开辟一块空间,这块空间就是函数栈帧。博主通过一张如下图片来慢慢解析。
可能有的uu会有些疑惑,为什么博主不画main函数的栈帧呢,uu们不要着急此时main函数的函数栈帧还没有创建呢,接下来博主将通过反汇编指令来带着大家一步一步地解析。
首先我们右击鼠标,然后点击转到反汇编,跳到反汇编后,就能看到如下的汇编代码!
估计大多数uu们看到这段汇编代码都是晕晕的感觉,没关系,博主将一步一步地带着大家去解析!
First:
首先第一步是进行push,压栈操作,将ebp寄存器的值压入到栈顶,在之前我们了解过,每一次压栈栈顶指针esp指向的地址也会发生变化,此时esp寄存器里头存放的是寄存器ebp的值。uu们如果想详细地了解的话,可以打开调试里面的监视和内存去观看寄存器esp的变化。
push前
push后
通过push前与push后的对比,我们可以清楚地发现,栈顶指针的地址发生了变化,减小了,在之前我们了解过,栈是向下增长的,也就是从高地址到低地址进行增长,那么此时所对应的函数栈帧图如下图。
Second:
第二步进行mov:数据移动指令,将寄存器esp的值赋给ebp,那么此时esp与ebp都指向栈顶。
mov前
mov后
通过mov前与move后的对比,我们可以清楚地发现此时ebp与esp的值相等,两个寄存器都指向栈顶,那么此时所对应的函数栈帧图如下图!
Third:
第三步执行sub指令,将寄存器栈顶指针的值减去0E4h(16进制),那么此时栈顶指针往低地址走。
sub前
sub后
通过对比sub前与sub后,我们会发现此时esp和ebp不再维护最初的空间,而是维护了一块新的空间,我们之前说过,函数每次调用都会在栈区上开辟一块属于自己的函数栈帧,那么此时栈顶指针esp与栈底指针ebp所维护的空间就是为main函数所开辟的函数栈帧,所对应的函数栈帧图如下。
Fourth
第四步:连续push三次,也就是向栈中压入三个值,分别为ebx,esi,edi。我们之前说过,每进行一次压栈,栈顶指针esp都会向下增长(向低地址增长)。
三次push前
三次push后
通过对比三次push前与三次push后,esp的值此时减少了,此时所对应的函数栈帧图如下图
Fifth:
第五步:执行lea指令,lea指令的全称呼是load effective address(加载有效地址); 将ebp-24h这个地址的值存放到寄存器edi中,我们可以通过监视来观察。
lea前
lea后
Sixth
第六步:执行两次mov指令,将9(16进制)与0CCCCCCCCh分别存放到寄存器ecx与eax中。
mov前:
mov后:
通过两次mov的对比,我们会发现此时寄存器ecx中存储的值为9,eax中的值为16进制的0xcccccccc。
seventh
第七步:从ebp的位置开始到edi的位置也就是ebp - 24h的位置,将eax中的值拷贝到这两个寄存器所维护的区域,每次拷贝dword(doubleword)双字,一个字占2个字节,双字占四个字节,总共拷贝 ecx次也就是9次。
拷贝前
拷贝后
对比拷贝前与拷贝后,我们可以发现,ecx的值从9变为了0,也就是总共拷贝了九次,每次拷贝4个字节!此时所对应的函数栈帧图如下图
eighth
第八步,创建局部变量a并且为其赋值,在将0Ah(10)(存储到ebp - 8的位置上,实际上在ebp - 8的位置上就是局部变量a。
mov前
mov后
通过mov前后的对比,此时在ebp-8的位置上从最初的cccccccc变成了0x0000000a也就是10。此时所对应的函数栈帧图如下
ninth
创建局部变量b并且为其赋值,在将014h(20)存储到ebp - 14h(ebp - 20)的位置上,实际上在ebp - 14h的位置上就是局部变量b。
mov前
mov后
通过对比mov前与mov后,此时在ebp-14h的位置上从最初的cccccccc变成了0x00000014也就是20。此时所对应的函数栈帧图如下
tenth
将ebp - 14h这个位置所存储的四个字节的数据存放到寄存器eax中,然后再进行压栈,接着再将ebp - 8这个位置所存储的四个字节的数据存放到寄存器ecx中,然后再进行亚栈。PS:通过之前的操作我们可以得知,ebp - 14h所存储的值就是局部变量b,ebp - 8这个位置所存储的值就是局部变量a。这几步操作是在调用Add函数前进行传参
此时所对应的函数栈帧图如下。可能家人们对这几步操作还不是特别地理解,没关系,我们先向下看!
eleventh
这一步,我们将要执行Call指令即调用函数,在执行Call指令前我们首先记住这个地方的地址,然后这个时候uu们就不要按F10了,按F11,此时我们再观察一下,栈顶指针
的位置
call前
call后
在call之前,我们可以观察到,栈顶指针里面存储的是00 00 00 0a也就是20,这是一开始压栈所影响的,在call之后,esp的地址发生了变化,家人们有木有觉得此时栈顶指针esp所存储的内容是不是很熟悉,没错!在调用的同时,会将call指令的下一条指令的地址进行压栈,那么此时所对应的函数栈帧图如下。至于它的意义何在呢?我们先继续往下看!
twelfth
执行完第11步后,此时我们再按F11,这时候算是真正来到了Add函数里面,来到Add函数里面,仔细观看一下,前面这一部分,是不是跟main函数的情况大同小异,也就是在为我的Add函数开辟函数栈帧。这里博主再带家人们走一遍步骤.
thirteenth
此时进入了Add函数后,首先进行push,此时将ebp的值进行压栈(这个ebp是main函数的ebp),我们知道ebp为栈底指针,ebp之前一直在维护main函数的函数栈帧,此时对其压栈,就等价于是将main的ebp进行压栈。
push前
push后
对比push前与push后,我们可以清楚地发现此时栈顶指针esp指向的地址发生了变化,减小了。此时所对应的函数栈帧图如下。
fourteenth
第十五步:进行mov操作,将寄存器esp的值赋给ebp,那么此时esp与ebp都指向栈顶。
mov前
mov后
对比mov前与mov后,我们可以清楚地看到,此时esp与ebp所指向的地址是一样的,此时所对应的函数栈帧图如下
fifteenth
此时将执行sub指令,将寄存器栈顶指针的值减去0CCh(16进制),那么此时栈顶指针往低地址走。
sub前
sub后
通过过对比sub前与sub后,我们会发现此时esp和ebp不再维护main函数的函数栈帧,而是维护了一块新的栈帧,我们之前说过,函数每次调用都会在栈区上开辟一块属于自己的函数栈帧,那么此时栈顶指针esp与栈底指针ebp所维护的空间就是为Add函数所开辟的函数栈帧,所对应的函数栈帧图如下。
sixteenth
第16步,连续push三次,也就是向栈中压入三个值,分别为ebx,esi,edi。我们之前说过,每进行一次压栈,栈顶指针esp都会向下增长(向低地址增长)。
三次push前
三次push后
通过对比三次push前与三次push后,esp的值此时减少了,此时所对应的函数栈帧图如下图
Seventeenth
第十七步,执行lea指令,lea指令的全称呼是load effective address(加载有效地址); 将ebp-0Ch这个地址的值存放到寄存器edi中,我们可以再次通过监视来观察。
lea前
lea后
eighteenth
第十八步,执行两次mov指令,将3(16进制)与0CCCCCCCCh分别存放到寄存器ecx与eax中。
mov前
mov后
通过两次mov的对比,我们会发现此时寄存器ecx中存储的值为3,eax中的值为16进制的0xcccccccc。
nineteenth
第十九步,从ebp的位置到edi也就是ebp - 0ch的位置开始,将eax中的值拷贝到这两个寄存器所维护的空间,每次拷贝dword(doubleword)字,一个word占两个字节,douleword占四个字节,总共拷贝ecx次也就是3次。
拷贝前
拷贝后
对比拷贝前与拷贝后,我们可以发现,ecx的值从3变为了0,也就是总共拷贝了三次,每次拷贝4个字节!此时所对应的函数栈帧图如下图。
twentieth
第20步,将ebp + 8这一个位置所指向的值,存放到eax寄存器中,我们可以通过观察函数栈帧图,ebp + 8所指向的值,正好是我Add函数里头形式参数a,没错,就是就形参a的值mov到寄存器eax中!
mov前
mov后
通过对比mov前与mov后,我们可以发现,此时eax中的值为16进制的 0x00 00 00 0a(即十进制的10)
twenty-first
第二十一步,执行Add命令,将ebp + 0Ch所指向的值加到eax寄存器中,我们通过观察函数栈帧图可以发现,ebp + 0Ch所指向的值正好是形参b,没错,就是将变量b的值加到eax寄存器中。
Add前
Add后
通过对比Add前与Add后,我们可以发现,此时eax中的值为16进制的 0x00 00 00 1e(即十进制的30)
Twenty-Second
将eax中的值mov到ebp - 8 的位置上,之前的mov与add操作使得寄存器eax中所存储的值为30,也就是说此时ebp - 8的位置所存放的值是30并且由变量value所接收,那么也就是说ebp - 8的位置上存储的就是局部变量value。
mov前
mov后
通过对比mov前与mov后,我们可以发现此时ebp - 8的位置上存储的确实是寄存器eax中的值也就是十六进制的0x 00 00 00 1e(十进制的30),此时所对应的函数栈帧图如下
Twenty-Third
第二十三步,将ebp - 8这个地址所指向的值(也就是变量value的值)存放到寄存器eax中,我们知道,寄存器不会随着程序的退出而销毁,与此同时,变量value一会出Add函数后会销毁,因此先存放到寄存器eax中。
Twenty-fourth
第二十四步,连续进行三次pop,分别将edi,esi,ebx这三个值从栈顶弹出并且弹到edi寄存器,esi寄存器,ebx寄存器中,同时esp栈顶寄存器也要发生改变,每次弹出esp的值会 + 4。
三次pop前
三次pop后
通过对比三次pop前与三次pop后,我们可以发现,此时esp的值加了 12,此时所对应的函数栈帧图如下图。
Twenty-fifth
第二十五步:这一步操作,将ebp赋给esp,这一步的操作就是在销毁Add函数的函数栈帧,因为函数调用完了,我的变量value值存放在了eax寄存器,因此此时通过mov操作将ebp的值赋给esp来销毁Add函数的函数栈帧。此时所对应的函数栈帧图如下图。
Twenty-sixth
第二十六步:将栈顶元素弹出到寄存器ebp中,我们可以发现此时的栈顶元素是main函数的ebp的地址值,那么为什么会在这里存放main函数的ebp的地址值呢,是因为随着函数栈帧的销毁,main函数的栈顶是很容易找到的,但是main函数的栈底是难以找到的,因此将main函数的栈底ebp的地址值存储值,当Add函数调用完以后,进行pop,此时ebp就回到了main函数的栈底了。pop的同时栈顶指针esp也会 + 4。
PS:博主在这里经过了多次调试,所以可能call指令的下一条指令的地址值会有些不同,家人们这里要注意一下哦
pop前
pop后
通过对比pop前与pop后,我们可以发现此时esp的地址值增加了4,ebp的值也发生了变化,此时所对应的函数栈帧图如下。
Twenty-Seventh
第二十七步:执行ret指令,我们仔细观看函数栈帧,此时栈顶上存储着最初调用Add函数的call指令的下一条指令的地址,在执行ret指令的同时此时会在栈顶弹出call指令的下一条指令的地址,那么此时ret指令就是回到执行Add函数时的call指令的下一条地址。**那么家人们可以思考一下,为什么我们要存放call指令的下一条指令的地址呢?其目的是,当调用完函数以后,我还能回来,回来之后还能够从call指令的下一条指令开始执行。**此时所对应的函数栈帧图如下。
Twenty-eighth
第二十八步:对esp寄存器执行 + 8的操作,我们观察函数栈帧图,此时在栈中还存在着调用Add函数的形参变量a与形参变量b,Add函数已经调用完了,那么此时既要销毁函数栈帧又要销毁形参,此时这一步操作就是用来销毁形参的,将其还给操作系统。
Add前
Add后
通过对比Add前与Add后,我们可以发现,esp的值发生了变化,此时所对应的函数栈帧图如下
Twenty-ninth
第二十九步,执行mov操作,将eax中的值mov到ebp - 20h的位置上,我们会发现,刚刚调用完Add函数的结果会存放变量result中,而result变量就是在ebp - 20h的位置上,然后我们再想一下,eax中的值是什么,就是我们在调用Add函数时求出来的和存放到了eax寄存器中,那么也就说此时ebp - 20h位置上的值就是调用Add函数时求出来的和。
mov前
mov后
通过对比mov前与mov后,我们可以发现,此时ebp - 20h上的值就是eax寄存器中所存放的值,此时所对应的函数栈帧图如下
Thirtieth
剩下的操作就是执行printf函数,将其打印在屏幕上,然后再销毁main函数的函数栈帧,这里博主就不再细讲啦,uu下去以后可以结合博主讲解的Add函数栈帧的销毁过程去理解哦!
4.:前期问题解答
4.1:局部变量是如何创建的
局部变量的创建,首先是为函数开辟好栈帧空间,栈帧空间里头,初始化一部分空间以后,然后再在栈帧空间里头对局部变量进行分配空间。
4.2:为什么局部变量的值不初始化时是随机值?
函数栈帧在开辟好以后会对一部分空间进行初始化,而初始化的值是随机值,这也就是为什么局部变量在不初始化时会是随机值。
4.3:函数是怎么传参的?传参的顺序是怎么样的
当我们要调用函数时,其实在还没调用前,通过不断地push,从右向左开始压栈,压入到栈中,当真正进入到函数里头,通过指针的偏离量来找到形参。
4.4:形参和实参是什么关系?
形参是我们压栈时为其开辟的空间,这块空间与实参的空间是相互独立的,两块空间只是存储的值一致,因此我们才会说形参是实参的一份临时拷贝,形参的改变影响不到实参。
4.5: 函数是怎么做的以及函数调用结束后怎么返回的?
当我们在调用函数的同时即执行call指令时,call指令会将下一条执行指令的地址值压入栈中与此同时又会将上一个函数栈帧中的ebp的地址值也压入栈中,在调用完函数后要返回时,通过pop ebp找到上一个函数的栈顶的地址值,从令esp栈顶指针与ebp栈底指针重新维护这块函数栈帧与此同时会销毁此时调用的Add函数的函数栈帧,然后通过在栈中压入的call指令的下一条指令的地址值跳转到与之对应的位置,令函数返回,返回值则是通过在函数调用完之前事先存放在寄存器中被带回来的。
好啦,家人们,关于函数栈帧的创建与销毁这块的相关细节知识,博主就讲到这里了,如果uu们觉得博主讲的不错的话,请动动你们滴滴给博主点个赞,你们滴鼓励将成为博主源源不断滴动力!
相关文章:

[内功修炼]函数栈帧的创建与销毁
文章目录 1:什么是函数栈帧2:理解函数栈帧能解决什么问题呢3:函数栈帧的创建与销毁的解析3.1:什么是栈3.2:认识相关寄存器与汇编指令相关寄存器相关汇编指令 3.3 解析函数栈帧的创建和销毁3.3.1 预备知识3.3.2 详细解析一:调用main函数,为main函数开辟函数栈帧First:push前push…...

【深度学习-目标检测】03 - Faster R-CNN 论文学习与总结
论文地址:Faster R-CNN: Towards Real-Time ObjectDetection with Region Proposal Networks 论文学习 1. 摘要与引言 研究背景与挑战:当前最先进的目标检测网络依赖于 区域提议(Region Proposals)来假设目标的位置,…...
oracle11体系结构二-存储结构
数据区: 数据区(数据扩展区)由一组连续的oracle数据块所构成的存储结构,一个或多个数据块组成一个数据区,一个或多个数据区组成一个段。当段中所有空间被使用完后,oracle系统将自动为该段分配一个新的数据…...

如何通过内网穿透实现远程访问本地Linux SVN服务
文章目录 前言1. Ubuntu安装SVN服务2. 修改配置文件2.1 修改svnserve.conf文件2.2 修改passwd文件2.3 修改authz文件 3. 启动svn服务4. 内网穿透4.1 安装cpolar内网穿透4.2 创建隧道映射本地端口 5. 测试公网访问6. 配置固定公网TCP端口地址6.1 保留一个固定的公网TCP端口地址6…...

网页乱码问题(edge浏览器)
网页乱码问题(edge) 文章目录 网页乱码问题(edge)前言一、网页乱码问题1.是什么:(描述)2.解决方法:(针对edge浏览器)(1)下载charset插…...

泛微OA xmlrpcServlet接口任意文件读取漏洞(CNVD-2022-43245)
CNVD-2022-43245 泛微e-cology XmlRpcServlet接口处存在任意文件读取漏洞,攻击者可利用漏洞获取敏感信息。 1.漏洞级别 中危 2.影响范围 e-office < 9.5 202201133.漏洞搜索 fofa 搜索 app"泛微-OA(e-cology)"4.漏洞复现 …...

MATLAB ga函数的使用方法
一、ga句法结构 x ga(fitnessfcn,nvars) x ga(fitnessfcn,nvars,A,b) x ga(fitnessfcn,nvars,A,b,Aeq,beq) x ga(fitnessfcn,nvars,A,b,Aeq,beg,IB,UB) x ga(fitnessfcn,nvars,A,b,Aeq,beq,LB,UB,nonlcon) x ga(fitnessfcn,nvars,A,b,Aeq,beq,LB,UB,nonlcon,options) x …...

基于STM32和MQ-2传感器的无线烟雾检测系统设计
随着科技的不断发展,人们对生活安全的要求也越来越高。其中,烟雾检测系统在预防火灾方面起着至关重要的作用。本文将介绍一种基于STM32和MQ-2传感器的无线烟雾检测系统设计,旨在实时检测环境中的烟雾,并及时发出警报,以…...

华为vrrp+mstp+ospf+dhcp+dhcp relay配置案例
1、左边是vlan 10主桥,右边是vlan 20的主桥,并且互为备桥 2、 vlan 10 vrrp网关默认用左边,vlan 20的vrrp 网关默认用右边,对应mstp生成树 3、两边都track检测,不通就把vrrp减掉60,这样就会自动切另一边了 …...
5-Docker实例-tomcat application
1.安装如下树形结构创建目录及文件,内容如下: 目录结构: [root@centos79 ~]# tree demo demo ├── index.html └── WEB-INF└── web.xml1 directory, 2 files [root@centos79 ~]# index.html文件内容 [root@centos79 demo]# cat index.html <h1>hello dock…...

Pikachu靶场 “Http Header”SQL注入
1. 先在 pikachu 打开 Http Header 注入模块,点击提示 查看登录 账号 和 密码,登陆后去 Burp 中找到登陆的 GET请求 2. 设置payload1 :在 User-Agent最后 输入 查看 数据库名 or updatexml(1,concat(0x7e,database()),0) or 查看 用户名…...

OpenEuler安装内网穿透工具实现ssh连接openEuler系统
文章目录 1. 本地SSH连接测试2. openEuler安装Cpolar3. 配置 SSH公网地址4. 公网远程SSH连接5. 固定连接SSH公网地址6. SSH固定地址连接测试 本文主要介绍在openEuler中安装Cpolar内网穿透工具实现远程也可以ssh 连接openEuler系统使用. 欧拉操作系统(openEuler, 简称“欧拉”…...
【效率工具】利用python进行本地知识库(PDF和WORK文件内容)的批量模糊搜索
目录 前言 一、为什么要进行本地文档的批量搜索? 二、如何去做呢?...

快速入门学习定时任务框架-xxljob
定时任务框架-xxljob 简介 主要用于分布式任务调度,可以将任务调度和执行分布在多个节点上。它提供了一个集中式的管理平台,支持动态添加、修改、删除任务,以及任务的分片执行,确保任务在分布式环境中的高可用性的一个框架 spr…...

Floyd(弗洛伊德)算法总结
知识概览 Floyd算法适合解决多源汇最短路问题,其中源点是起点,汇点是终点。时间复杂度是。 例题展示 题目链接 活动 - AcWing 系统讲解常用算法与数据结构,给出相应代码模板,并会布置、讲解相应的基础算法题目。https://www.acw…...

西南科技大学计算机网络实验二 (IP协议分析与以太网协议分析)
一、实验目的 通过分析由跟踪执行traceroute程序发送和接收捕获得到的IP 数据报,深入研究在IP 数据报中的各种字段,理解IP协议。基于ARP命令和Ethereal进行以太网帧捕获与分析,理解和熟悉ARP协议原理以及以太网帧格式。 二、实验环境 与因特网连接的计算机网络系统;主机操…...

SICP : The Elements of Programming
好的计算机编程语言应具备的三个特性 基础单元表达式,计算机编程语言最最最基础单元,理应具备的表达式组合的能力,能够通过基础单元表达式组合成更复杂的元素抽象的能力,能通过复杂的元素抽象成更高层的单元 基础单元表达式 加 …...

支付宝、学习强国小程序input、textarea数据双向绑定
前言 和 vue 的绑定有些区别,需要注意。直接 value"{{inputValue}}" 是无法双向绑定的。 正确思路 文档说的比较详细,不过没有组合使用的案例,需要自行理解。这里正确的方法是先用 value 绑定数据,再使用 onInput 事件…...

AI“百模大战”现状:向垂直、B端谋场景,算力仍是主要制约因素
文章目录 每日一句正能量前言AI(人工智能)大模型正“飞入”百姓家和行业中。向垂直、B端谋场景算力仍是主要制约因素构建“数据-模型-应用”飞轮后记 每日一句正能量 我们必须在失败中寻找胜利,在绝望中寻求希望。 前言 在当前快速发展的人工…...
手机上的软件怎么修改网络IP地址
在手机上修改网络IP地址通常需要通过以下两种方法: 1. 使用VPN(虚拟私人网络)或代理软件: 步骤如下: - 下载并安装一个可靠的VPN或代理软件到你的手机上。 - 打开VPN或代理软件,选择一个你希望获取IP地址…...
多场景 OkHttpClient 管理器 - Android 网络通信解决方案
下面是一个完整的 Android 实现,展示如何创建和管理多个 OkHttpClient 实例,分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...

ServerTrust 并非唯一
NSURLAuthenticationMethodServerTrust 只是 authenticationMethod 的冰山一角 要理解 NSURLAuthenticationMethodServerTrust, 首先要明白它只是 authenticationMethod 的选项之一, 并非唯一 1 先厘清概念 点说明authenticationMethodURLAuthenticationChallenge.protectionS…...
C++.OpenGL (10/64)基础光照(Basic Lighting)
基础光照(Basic Lighting) 冯氏光照模型(Phong Lighting Model) #mermaid-svg-GLdskXwWINxNGHso {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-GLdskXwWINxNGHso .error-icon{fill:#552222;}#mermaid-svg-GLd…...
Rust 异步编程
Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...
在Ubuntu24上采用Wine打开SourceInsight
1. 安装wine sudo apt install wine 2. 安装32位库支持,SourceInsight是32位程序 sudo dpkg --add-architecture i386 sudo apt update sudo apt install wine32:i386 3. 验证安装 wine --version 4. 安装必要的字体和库(解决显示问题) sudo apt install fonts-wqy…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...
学习一下用鸿蒙DevEco Studio HarmonyOS5实现百度地图
在鸿蒙(HarmonyOS5)中集成百度地图,可以通过以下步骤和技术方案实现。结合鸿蒙的分布式能力和百度地图的API,可以构建跨设备的定位、导航和地图展示功能。 1. 鸿蒙环境准备 开发工具:下载安装 De…...
SQL Server 触发器调用存储过程实现发送 HTTP 请求
文章目录 需求分析解决第 1 步:前置条件,启用 OLE 自动化方式 1:使用 SQL 实现启用 OLE 自动化方式 2:Sql Server 2005启动OLE自动化方式 3:Sql Server 2008启动OLE自动化第 2 步:创建存储过程第 3 步:创建触发器扩展 - 如何调试?第 1 步:登录 SQL Server 2008第 2 步…...
深入浅出JavaScript中的ArrayBuffer:二进制数据的“瑞士军刀”
深入浅出JavaScript中的ArrayBuffer:二进制数据的“瑞士军刀” 在JavaScript中,我们经常需要处理文本、数组、对象等数据类型。但当我们需要处理文件上传、图像处理、网络通信等场景时,单纯依赖字符串或数组就显得力不从心了。这时ÿ…...

基于django+vue的健身房管理系统-vue
开发语言:Python框架:djangoPython版本:python3.8数据库:mysql 5.7数据库工具:Navicat12开发软件:PyCharm 系统展示 会员信息管理 员工信息管理 会员卡类型管理 健身项目管理 会员卡管理 摘要 健身房管理…...