函数栈帧的创建和销毁【汇编语言理解】

🌹作者:云小逸
📝个人主页:云小逸的主页
📝Github:云小逸的Github
🤟motto:要敢于一个人默默的面对自己,强大自己才是核心。不要等到什么都没有了,才下定决心去做。种一颗树,最好的时间是十年前,其次就是现在!学会自己和解,与过去和解,努力爱自己。==希望春天来之前,我们一起面朝大海,春暖花开!==🤟
👏专栏:C++👏 👏专栏:Java语言👏👏专栏:Linux学习👏
👏专栏:C语言初阶👏👏专栏:数据结构👏👏专栏:备战蓝桥杯👏

文章目录

  • 前言
  • 1. 什么是函数栈帧
  • 2. 理解函数栈帧能解决什么问题呢?
  • 3. 函数栈帧的创建和销毁解析
    • 3.1 什么是栈?
    • 3.2 认识相关寄存器和汇编指令
      • 3.3 解析函数栈帧的创建和销毁
        • 3.3.1 预备知识
        • 3.3.2 函数的调用堆栈
          • 演示代码:
          • 深度剖析:
    • 总结
      • 1.局部变量是怎么创建的?
      • 2.为什么局部变量的值是随机值?
      • 3.函数是怎么传参的?传参的顺序是怎样的?
      • 4.形参和实参是什么关系?
      • 5.函数调用是怎么做的?
      • 6. 函数调用是结束后怎么返回的?
  • 最后


前言

在前面学习学习C++引用的时候,有不少同学私信我,说对于函数栈帧那一块不是很理解,那么今天我们就来系统地学习一下函数栈帧的创建和销毁,码字不易,希望多多支持!!!

00117AF9.jpg

1. 什么是函数栈帧

在编写 C 语言代码时,我们通常会将一个独立的功能抽象为函数,因此 C 程序是以函数为基本单位的。
那么函数是如何调用的?函数的返回值又是如何返回的?函数参数是如何传递的?这些问题都与函数栈帧有关。


函数栈帧(stack frame)是指在程序的调用栈(call stack)中,函数调用过程中开辟的空间,用于存放以下内容:

  • 函数参数和返回值
  • 临时变量(包括函数的非静态的局部变量以及编译器自动生产的其他临时变量)
  • 保存上下文信息(包括在函数调用前后需要保持不变的寄存器)。

2. 理解函数栈帧能解决什么问题呢?

理解函数栈帧可以解决以下问题:

  • 局部变量是如何创建的?
  • 为什么局部变量的内容未初始化时是随机的?
  • 函数调用时参数是如何传递的?传参的顺序是怎样的?
  • 函数的形参和实参分别是如何实例化的?
  • 函数的返回值是如何返回的?

3. 函数栈帧的创建和销毁解析

3.1 什么是栈?

  • 栈(stack)是现代计算机程序里最为重要的概念之一,几乎每一个程序都使用了栈,没有栈就没有函数,没有局部变量,也就没有我们如今看到的所有的计算机语言。

  • 在经典的计算机科学中,栈被定义为一种特殊的容器,用户可以将数据压入栈中(入栈,push),也可以将已经压入栈中的数据弹出(出栈,pop),但是栈这个容器必须遵守一条规则:先入栈的数据后出栈(First In Last Out, FIFO)。就像叠成一叠的书,先叠上去的书在最下面,因此要最后才能取出。

  • 在计算机系统中,栈则是一个具有以上属性的动态内存区域。程序可以将数据压入栈中,也可以将数据从栈顶弹出。压栈操作使得栈增大,而弹出操作使得栈减小。

  • 在经典的操作系统中,栈总是向下增长(由高地址向低地址)的。

  • 在我们常见的 i386 或者 x86-64 下,栈顶由成为 esp 的寄存器进行定位。

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记录的是栈顶的地址。

image.png

  1. 函数栈帧的创建和销毁过程,在不同的编译器上实现的方法大同小异,本次演示以VS2019为例。

3.3.2 函数的调用堆栈

演示代码:
#include <stdio.h>
int Add(int x, int y)
{
	int z = 0;
	z = x + y;
	return z;
}
int main()
{
	int a = 3;
	int b = 5;
	int ret = 0;
	ret = Add(a, b);
	printf("%d\n", ret);
	return 0;
}

image.png

在我们平时写的代码中一般都是在main函数调用其他函数,但是我们是否知道其实main函数也是被调用的!!!
我们可以通过vs中的调试来看到这个过程

CT-20230423073404.png

CT-20230423073453.png

image.png
调用顺序是下面的调用上面的

Snipaste_2023-04-23_15-38-27.png

深度剖析:

从上面的知识点,我们可以知道:是通过__tmainCRTStartup函数来调用main函数,因此先建立__tmainCRTStartup函数的栈帧:

1.png
在这里设定下面是高地址,下面是低地址,从高地址想低地址入栈。
好的,我们接下来来看汇编指令:

2.png


int main()

{


  • 007C1940 push ebp
    这一步是将ebp入栈,然后将esp向低地址移动一位(4个字节)到指向push进来的ebp,
    如下图所示:
    3.png

4.png


  • 007C1941 mov ebp,esp
    mov是将后者的值给前者,即将esp赋值给ebp

5.png


  • 007C1943 sub esp,0E4h
    是指将esp减去0E4h(十进制为228),即esp指向的地址变小,指向了上面的某块区域了:

6.png
此时ebp和esp之间存在一块空间就是为main函数预开辟的空间


  • push ebx 将 ebx 的值压入堆栈,堆栈指针 esp 减少 4 个字节。
  • push esi 将 esi 的值压入堆栈,堆栈指针 esp 再减少 4 个字节。
  • push edi 将 edi 的值压入堆栈,堆栈指针 esp 再减少 4 个字节。 这样,ebx、esi 和 edi 的值就被保存在堆栈中,可以在函数执行过程中随时使用,不会被修改。在函数返回时,再通过 pop 指令将这三个寄存器的值恢复到原来的状态。
    7.png

  • lea edi, [ebp-24h] 将 [ebp-24h] 的地址加载到 edi 中,此时 edi 指向分配的内存空间(main函数)的起始地址。
  • mov ecx, 9 将 ecx 设置为 9,表示需要将 9 个 dword(4 字节)内存单元设置为 0xCCCCCCCC。
  • mov eax, 0CCCCCCCCh 将 eax 设置为 0xCCCCCCCC,即将要填充的值。
  • rep stos dword ptr es:[edi] 重复执行将 eax 中的值写入 edi 指向的内存位置,直到 ecx 个内存单元都被填充为止。这样就完成了堆栈内存空间的初始化。

8.png
这个也是为什么如果你不定义一个变量,不初始化,打印的时候会是随机值!!!

到这里为mian函数栈帧的开辟准备工作才完成


  • mov ecx,0AEC003h 将值 0AEC003h 存储到 ECX 中,即将函数 Add 的地址作为参数传递给下一条指令。
  • call 00AE131B 调用函数 Add。此指令将当前指令的下一条指令的地址(即返回地址)压入堆栈,并跳转到函数 Add 的入口地址开始执行函数体。在函数执行完后,再通过 ret 指令返回到调用该函数的指令处继续执行。
    为调用add函数做准备

10: 	int a = 3;
  • mov dword ptr [ebp-8], 3 将值 3 存储到 [ebp-8] 中,即将变量 a 的值设置为 3。

9.png
没有赋值默认就是0CCCCCCCCh(随机值,烫烫烫那种)


11: 	int b = 5;
  • mov dword ptr [ebp-14h], 5 将值 5 存储到 [ebp-14h] 中,即将变量 b 的值设置为 5。

10.png
与a隔8个字节


12: 	int ret = 0;
  • mov dword ptr [ebp-20h],0 将值 0 存储到 [ebp-20h] 中,即将变量 ret 的值初始化为 0。

13: 	ret = Add(a, b);
  • mov eax,dword ptr [ebp-14h] 将 [ebp-14h] 中的值(即变量 b 的值)存储到 EAX 中。
  • push eax 将 EAX 中的值压入堆栈,作为第二个参数传递给函数 Add。

image.png

  • mov ecx,dword ptr [ebp-8] 将 [ebp-8] 中的值(即变量 a 的值)存储到 ECX 中。
  • push ecx 将 ECX 中的值压入堆栈,作为第一个参数传递给函数 Add。

image.png

  • call 00AE11BD call 指令会将当前代码的下一条指令的地址(即 call 指令的下一条指令)压入堆栈,并将程序的执行控制权转移到函数的入口地址。函数执行完毕后,会返回到 call 指令的下一条指令继续执行。在函数调用过程中,参数会被压入堆栈中,函数执行完毕后返回值也会被存储在指定的寄存器中,通常是 EAX 寄存器。因此,在执行 call 00AE11BD 指令之前,需要确保函数的入口地址是有效的,并且函数参数和返回值的处理符合函数定义的要求。

    call指令在执行的过程中,还会把call指令的下一个指令压到栈中:

11.png


这里会执行jmp指令,进行跳转:

  • 00AE131B jmp 00AE19E0 是一条汇编指令,它的作用是将程序的执行控制权无条件地转移到内存地址 00AE19E0 所在的代码处,从而实现代码的跳转。具体来说,该指令会直接将程序的执行控制权转移到指定的地址,不会对跳转前的指令进行任何处理,也不会保留跳转前的任何状态。因此,在执行该指令之前,需要确保跳转的目标地址是有效的,否则可能会导致程序出错或崩溃。

下面这几步和main函数建立的类似:

  1. push ebp:把当前函数的基址指针(EBP)压入堆栈中,为后续的指令执行做准备。
  2. mov ebp, esp:将堆栈指针(ESP)的值赋给基址指针(EBP),这样就可以在函数内部通过基址指针来访问函数参数和局部变量了。
  3. sub esp, 0CCh:为函数的局部变量分配空间,这里是分配了 204(即 0xCC)字节的空间。 这些指令一般出现在函数的开头部分,用于初始化函数的堆栈和局部变量。第三条指令中的 0xCC 可能是根据具体的函数需要来确定的,一般是根据函数内部需要使用的局部变量的大小来决定的。需要注意的是,在函数执行完毕后,需要通过恢复堆栈指针(通过指令 mov esp, ebppop ebp)来释放堆栈空间,以免出现内存泄漏等问题。
    12.png

  1. push ebx:将 EBX 寄存器的值压入堆栈中,为后续的指令执行做准备。
  2. push esi:将 ESI 寄存器的值压入堆栈中,为后续的指令执行做准备。
  3. push edi:将 EDI 寄存器的值压入堆栈中,为后续的指令执行做准备。
  4. lea edi,[ebp-0Ch]:使用 LEA 指令计算出一个地址,该地址是基址指针减去 12(即 [ebp-0Ch]),并将其存储在 EDI 寄存器中。
  5. mov ecx,3:将计数器 ECX 的值设置为 3,以便用于重复执行指令。
  6. mov eax,0CCCCCCCCh:将一个特殊的值(0xCCCCCCCC)存储在 EAX 寄存器中。
  7. rep stos dword ptr es:[edi]:重复执行 STOS 指令,将 EAX 的值(即 0xCCCCCCCC)存储到 EDI 指向的内存地址中,直到计数器 ECX 的值变为 0。 这些指令一般出现在函数的开头部分,用于初始化函数的堆栈和局部变量。其中,第 4 条指令计算出的地址一般用于存储函数中一些需要初始化的变量。第 6 条指令将一个特殊的值存储在 EAX 寄存器中,该值通常用于调试目的,在程序运行时可以检测到是否访问了未初始化的内存地址。第 7 条指令则是重复执行 STOS 指令,将特定的值存储到指定的内存地址中,以便初始化变量。需要注意的是,在函数执行完毕后,需要通过恢复堆栈指针和寄存器的值(通过指令 pop edipop esipop ebxmov esp, ebppop ebp)来释放堆栈空间和还原寄存器的值,以免出现内存泄漏等问题。

13.png


 4: 	int z = 0;
  • 00AE1795 mov dword ptr [ebp-8],0:将常数值 0 存储到 EBP 减去 8 的地址处,即变量 z 的内存地址,以初始化 z 的值为 0。
    5: z = x + y;

  • 00AE179C mov eax,dword ptr [ebp+8] 从 EBP 加上 8 的地址处取出 4 字节的值,即变量 x 的值,并存储到 EAX 寄存器中。

  • 00AE179F add eax,dword ptr [ebp+0Ch] 将从 EBP 加上 12 的地址处取出的 4 字节值(即变量 y 的值)加到 EAX 寄存器中,得到结果并存储到 EAX 寄存器中。

  • 00AE17A2 mov dword ptr [ebp-8],eax 将 EAX 寄存器中的值(即 x + y 的结果)存储到 EBP 减去 8 的地址处,即变量 z 的内存地址中,完成变量 z 的赋值。

    6: return z;

  • 00AE17A5 mov eax,dword ptr [ebp-8] 从 EBP 减去 8 的地址处取出 4 字节的值,即变量 z 的值,并存储到 EAX 寄存器中。

    7: }

这段代码的作用是计算变量 x 和 y 的和,并将结果存储到变量 z 中,最后返回变量 z 的值。需要注意的是,变量 x、y、z 分别存储在 EBP 加上 8、12、8 的地址处,这是因为在函数调用时,参数和局部变量的值都存储在堆栈中,并通过 EBP 寄存器来访问。

image.png


00AE17A5 mov eax,dword ptr [ebp-8]
因为z是局部变量,出了add函数就会被销毁,因此将其赋值为一个全局变量(就是之前我在C++引用那篇文章中提到的【临时变量】)

解释:

这条汇编指令的作用是从 EBP 减去 8 的地址处取出 4 字节的值(即变量 z 的值),并将其存储到 EAX 寄存器中。这是因为在该函数的最后一行代码中,函数需要返回变量 z 的值,而该变量的值已经存储在 EBP 减去 8 的地址处,因此需要将其取出并存储到 EAX 寄存器中,以便作为函数的返回值。需要注意的是,这里的 dword ptr 是用来指定要取出的内存单元大小的关键词,它表示要取出 4 字节的值。


  1. pop edi:将堆栈顶部的值弹出并存储到 EDI 寄存器中,以恢复被保存的 EDI 寄存器值。
  2. pop esi:将堆栈顶部的值弹出并存储到 ESI 寄存器中,以恢复被保存的 ESI 寄存器值。
  3. pop ebx:将堆栈顶部的值弹出并存储到 EBX 寄存器中,以恢复被保存的 EBX 寄存器值。 这些寄存器值在函数调用时被保存在堆栈中,以便在函数执行过程中可以使用堆栈来保存临时变量和函数调用的返回地址等信息。在函数执行结束时,需要将这些被保存的寄存器值恢复到原始状态,以确保程序的正确性和稳定性。需要注意的是,恢复寄存器的顺序应该与保存寄存器的顺序相反。
    pop出栈弹出:

image.png


  1. add esp, 0CCh:将堆栈指针增加 0xCC(204)个字节的大小,以清空堆栈中的函数参数和局部变量等信息。这是因为在函数执行过程中,函数所使用的堆栈空间需要被释放,以便其他函数可以使用该空间。
  2. cmp ebp, esp:比较 EBP 寄存器中的值(即堆栈底部的地址)和 ESP 寄存器中的值(即当前堆栈指针的地址),以确保堆栈指针在函数执行结束后正确地回到了调用函数之前的位置。如果堆栈指针没有正确回退,可能会导致程序崩溃或出现未定义的行为。
  3. call 00AE1244:调用位于地址 0x00AE1244 处的子程序(或函数)。该函数的具体作用需要根据函数地址和函数实现来确定。在函数执行结束后,程序将从函数调用的下一条指令继续执行。

销毁add函数:

  1. mov esp, ebp:将 EBP 寄存器中的值(即堆栈底部的地址)赋值给 ESP 寄存器,以恢复堆栈指针到调用该函数之前的位置。这是因为在函数执行过程中,EBP 寄存器被用作堆栈帧指针,指向当前函数的栈帧。而在函数调用结束后,需要将堆栈指针恢复到调用该函数之前的位置,以便程序可以正确地返回到调用该函数的位置继续执行。
  2. pop ebp:从堆栈中弹出一个值,并存储到 EBP 寄存器中,以恢复被保存的 EBP 寄存器值。在函数执行过程中,EBP 寄存器被用作堆栈帧指针,指向当前函数的栈帧。在函数执行结束后,需要将 EBP 寄存器的值恢复到调用该函数之前的值,以确保程序的正确性和稳定性。
    通过移动ebp,esp进行销毁并返回到main函数

00AE17BB ret 作用:这条汇编指令的作用是从当前函数中返回,并将控制权交还给调用该函数的代码。具体来说,ret 指令会从堆栈中弹出一个值,该值被认为是函数的返回地址,然后将程序计数器(PC)设置为该地址,从而使程序跳转到该地址并继续执行。在函数执行结束时,需要使用 ret 指令来返回到调用该函数的代码处,以便程序可以继续执行。需要注意的是,函数的返回值通常存储在 EAX 寄存器中,并在调用该函数的代码中使用。

image.png


  • add esp,8 将堆栈指针 esp 加上 8,即弹出堆栈中的两个参数。
  • mov dword ptr [ebp-20h],eax 将 EAX 中的值(即 Add 的返回值)存储到 [ebp-20h] 中,即将变量 ret 的值设置为 Add 的返回值。

  14: 	printf("%d\n", ret);
  1. mov eax,dword ptr [ebp-20h]:将位于 EBP 寄存器减去 0x20(即堆栈中函数参数和局部变量的偏移量)处的 4 个字节的值(也就是变量 ret 的值)加载到 EAX 寄存器中,以便将其打印出来。
  2. push eax:将 EAX 寄存器中的值(即变量 ret 的值)复制一份,并将其压入堆栈中,以便作为 printf 函数的第一个参数。
  3. push 0AE7B30h:将地址 0AE7B30h 压入堆栈中,以便作为 printf 函数的第二个参数,该地址指向格式化字符串 “%d\n”。 然后,call 00AE10CD 指令调用 printf 函数来打印 ret 的值。最后,add esp,8 指令将堆栈指针增加 8 个字节的大小,以清除堆栈中的两个参数,以便程序可以正常返回。在函数返回之前,xor eax,eax 指令将 EAX 寄存器清零,以便将其作为返回值返回给调用该函数的代码,并结束该函数的执行。

  1. call 00AE10CD:调用位于地址 0x00AE10CD 处的子程序(或函数),该函数为 printf 函数。在调用该函数之前,push 指令将两个参数(即变量 ret 的值和格式化字符串 %d\n 的地址)压入堆栈中。函数调用完成后,将会返回到下一条指令(即 add esp,8)继续执行。
  2. add esp,8:将堆栈指针增加 8 个字节的大小,以清除堆栈中的两个参数。这是因为在函数调用中,参数需要被压入堆栈中,并在函数执行完成后被清除,否则可能会导致堆栈溢出或其他未定义行为。在该函数调用完成后,程序将从调用该函数的下一条指令继续执行。

15: 	return 0;

00AE190E xor eax,eax
这条汇编指令的作用是将 EAX 寄存器中的值清零,即将其设置为 0。这通常用于将 EAX 寄存器作为函数的返回值,并且希望返回值为 0 的情况。在这个例子中,该指令用于将 EAX 寄存器清零,以便将其作为函数的返回值,并返回给调用该函数的代码。

16: }

总结

1.局部变量是怎么创建的?

局部变量一般是在函数的栈帧中创建的,即在函数执行期间分配在堆栈上的内存空间。在函数开始执行时,会为所有的局部变量分配内存空间,并将其地址存储在堆栈上。在函数执行结束后,这些变量占用的内存空间会被释放,以便其他函数可以使用。

2.为什么局部变量的值是随机值?

局部变量的值是随机的,是因为在声明变量时没有对其进行初始化。在栈帧中分配给局部变量的内存空间可能包含任意值,这些值是由之前使用这些内存空间的代码留下的,与当前函数的代码逻辑无关。因此,如果不对局部变量进行初始化,它们的值就会是随机的。
随机值就是我们刚开始设定的ccccccccc(打印可能会出现烫烫烫等)

3.函数是怎么传参的?传参的顺序是怎样的?

函数传参通常是通过堆栈来完成的,在调用函数之前,参数会被压入堆栈中。通常,参数的传递顺序是从右向左,即最后一个参数会被先压入堆栈中,第一个参数会被最后压入堆栈中。

4.形参和实参是什么关系?

形参和实参是函数传递参数的两个概念。形参是在函数定义中声明的变量,用于接收函数调用时传递的实参值。实参是在函数调用中传递给函数的值。在函数调用时,实参的值会被传递给形参,并在函数内部使用。
两者是独立的空间

5.函数调用是怎么做的?

函数调用是通过 call 汇编指令完成的。该指令将函数的返回地址压入堆栈,并将程序计数器设置为函数的入口地址,以便跳转到函数的起始位置。在函数执行结束后,使用 ret 指令返回到调用该函数的代码处。

6. 函数调用是结束后怎么返回的?

函数调用结束后,返回值通常存储在 EAX 寄存器中,并通过 ret 指令返回给调用该函数的代码。在返回之前,堆栈指针会被恢复,以便清除函数调用期间压入堆栈的局部变量和其他参数。如果需要返回多个值,可以将它们打包成一个数据结构(如结构体)并将该结构体的指针返回。

最后

十分感谢你可以耐着性子把它读完和我可以坚持写到这里,送几句话,对你,也对我:

1. 人生最大的难度不是走出舒适区,而是保持冷静和自信面对不确定的未来

2. 人生就是一次次的选择,每个选择都会影响到未来的自己。

3. 没有比当下更好的时刻,因为未来和过去都只是自己的想象。

4. 人生中最重要的是学会珍惜当下,因为当下就是一切

5.划清和别人的界限。别人怎么看你,跟你毫无关系,你要怎么活,也跟别人没有任何关系,撇清别人,才能精力旺盛

最后如果觉得我写的还不错,请不要忘记点赞✌,收藏✌,加关注✌哦(。・ω・。)

愿我们一起加油,奔向更美好的未来,愿我们从懵懵懂懂的一枚菜鸟逐渐成为大佬。加油,为自己点赞!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/13674.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【C++的内联函数】

文章目录 一、什么是内联函数二、内联函数的优缺点三、使用内联函数的注意事项 一、什么是内联函数 用关键字inline修饰的函数叫做内联函数。 C编译器编译时会自动在被调用的地方展开。 二、内联函数的优缺点 内联函数的优点&#xff1a; 没有函数栈帧创建&#xff0c;提升…

WebRTC系列-Qos系列之AEC-可配置参数

文章目录 1. 简介2. 源码中相关参数WebRTC的自适应回声消除(AEC)是一个广泛使用的技术,用于在音频通信中消除扬声器输出产生的回声。在WebRTC中,有三种AEC算法可供选择,分别是 AECM、 AEC和 AEC3。本文将介绍WebRTC AEC 3算法的原理和应用场景。 在上图中可以看出AEC算…

应用于音箱领域中的音频功放IC型号推荐

音箱音频功放ic俗称“扩音机”又叫音频功率放大器IC&#xff1b;是各类音响器材中不可缺少的部分&#xff0c;其作用主要是将音源器材输入的较微弱信号进行放大后&#xff0c;产生足够大的电流去推动扬声器进行声音的重放。 现如今&#xff0c;音频功放芯片伴随着人工交互及智…

OpenShift 4 - 在 CI/CD Pipeline 中创建 KubeVirt 容器虚拟机 - 方法1+2 (视频)

《OpenShift / RHEL / DevSecOps 汇总目录》 说明&#xff1a;本文已经在支持 OpenShift 4.12 的 OpenShift 环境中验证 文章目录 准备环境安装可实现 KubeVirt 操作的 Tekton 资源创建密钥对 在 CI/CD 流水线管道中创建 VM方法1&#xff1a;通过 Manifest 任务创建 VM方法2&am…

自动驾驶企业面临哪些数据安全挑战?

近期&#xff0c;“特斯拉员工被曝私下分享用户隐私”不可避免地成了新闻热点&#xff0c;据说连马斯克也不能幸免。 据相关媒体报道&#xff0c;9名前特斯拉员工爆料在2019年至2022年期间&#xff0c;特斯拉员工通过内部消息系统私下分享了一些车主车载摄像头记录的隐私视频和…

spring框架注解

3.Spring有哪些常用注解呢&#xff1f; Spring常用注解 Web: Controller&#xff1a;组合注解&#xff08;组合了Component注解&#xff09;&#xff0c;应用在MVC层&#xff08;控制层&#xff09;。 RestController&#xff1a;该注解为一个组合注解&#xff0c;相当于Con…

MySql-高级( 面试问题简析) 学习笔记

文章目录 1. MySql 中 MyISAM 和 InnoDB 存储引擎区别1.1. MyISAM1.2. InnoDB 2. 索引的数据结构2.1. B Tree索引2.2. BTree索引2.3. MySql 做的优化 3. 为什么使用BTree索引而不使用Hash索引&#xff1f;4. 为什么使用BTree索引而不使用B-Tree索引&#xff1f;5. MyISAM 存储引…

设计模式-创建型模式之建造者模式

5. 建造者模式 5.1. 模式动机 无论是在现实世界中还是在软件系统中&#xff0c;都存在一些复杂的对象&#xff0c;它们拥有多个组成部分&#xff0c;如汽车&#xff0c;它包括车轮、方向盘、发送机等各种部件。而对于大多数用户而言&#xff0c;无须知道这些部件的装配细节&…

FPGA学习笔记(三):PLL 锁相环

在 FPGA 芯片内部集成了 PLL(phase-locked loop&#xff0c;锁相环)&#xff0c;可以倍频分频&#xff0c;产生其它时钟类型。PLL 是 FPGA 中的重要资源&#xff0c;因为一个复杂的 FPGA 系统需要不同频率、相位的时钟信号&#xff0c;一个 FPGA 芯片中 PLL 的数量是衡量 FPGA …

chatgpt智能提效职场办公-ppt怎么加音乐背景

作者&#xff1a;虚坏叔叔 博客&#xff1a;https://xuhss.com 早餐店不会开到晚上&#xff0c;想吃的人早就来了&#xff01;&#x1f604; 在 PowerPoint 中&#xff0c;您可以轻松地将音乐作为背景音乐添加到您的演示文稿中。下面是步骤&#xff1a; 打开您的 PowerPoint 演…

Linux文件类型与属性

一、文件类型 Linux 系统下一共分为 7 种文件类型。通过 stat 命令或者 ls 命令来查看文件类型。 - &#xff1a;普通文件 d &#xff1a;目录文件 c &#xff1a;字符设备文件 b &#xff1a;块设备文件 l &#xff1a;符号链接文件 s &#xff1a;套接字文件 p &…

关于今年五一调休。。

作者主页&#xff1a;爱笑的男孩。的博客_CSDN博客-深度学习,YOLO,活动领域博主爱笑的男孩。擅长深度学习,YOLO,活动,等方面的知识,爱笑的男孩。关注算法,python,计算机视觉,图像处理,深度学习,pytorch,神经网络,opencv领域.https://blog.csdn.net/Code_and516?typecollect个人…

11、HOOK原理上

一、HOOK 1.1 HOOK简介 HOOK,中文译为“挂钩”或“钩子”.在iOS逆向中是指改变程序运行流程的一种技术.通过hook可以让别人的程序执行自己所写的代码. 在逆向中经常使用这种技术重点要了解其原理,这样能够对恶意代码进行有效的防护. 1.2 Hook的应用场景 描述一个HOOK实用技…

【李老师云计算】实验一:Hadoop伪分布式集群部署与Eclipse访问Hadoop进行单词计数统计

索引 前言实验内容1. 安装虚拟机1.1 安装与激活1.2 ★解决使用虚拟机蓝屏(绿屏) 2. 安装CentOS2.1 下载CentOS2.2 VMware新建虚拟机2.3 安装CentOS(包括GUI、主机名)2.4 ★解决已经创建虚拟机改主机名 3. VMWare 网络配置3.0 使用VI编辑器和VMware3.0.1 使用VI编辑器3.0.2 使用…

遇到Spring事务失效,你该怎么办?

Spring 事务场景失效是一个常见的问题。今天来分析这个问题。 1、事务方法被final、static关键字修饰&#xff0c;方法访问权限不是public Service public class UserService {Autowiredprivate UserDao userDao;// final修饰的事务方法Transactionalpublic final void addUse…

技术干货|直流电源自动测试系统功能介绍

直流电源是一种将交流电转换为恒定电压或电流输出的电子设备。在实际生产生活中&#xff0c;直流电源被广泛应用于各种场合。但由于各种原因&#xff0c;包括工艺、质量等因素&#xff0c;直流电源存在一定的出厂偏差。为了确保直流电源的精度和稳定性&#xff0c;在生产过程中…

如何将模块加载到linux内核

一 顺利的情况 假设存在一个文件叫mymq.c,下该文件相同目录下的makefile如下语句&#xff1a; obj-y mymq.o 然后编译&#xff1a;编译完成了以后&#xff0c;mymq.c文件中&#xff0c;有个函数叫mymq_open,搜索这个函数在不在System.map文件中&#xff0c;如果在&#xff…

开放式耳机真的比封闭式强很多吗?推荐几款主流的开放式耳机

​开放式耳机&#xff0c;顾名思义&#xff0c;就是通过骨头振动来传导声音的耳机。相比于传统耳机&#xff0c;它的声音传输更加开放&#xff0c;不会对耳膜造成压迫感&#xff0c;也不会对耳膜旁的内毛细胞造成损害。因此开放式耳机既是运动蓝牙耳机&#xff0c;又是音乐蓝牙…

Spring依赖注入的三种方式使用及优缺点

初学Spring的时候,我们从Spring容器中获取Bean对象都是通过bean标签先将Bean对象注册到Spring容器中&#xff0c;然后通过上下文对象congtext的getBean方法进行获取&#xff0c;显然这种方法较为麻烦&#xff0c;所以有了更简单的存方法&#xff1a;五大类注解&#xff1b;取方…

在Linux中进行Jenkins-2.190的安装及使用

Jenkins-2.190安装在公网IP为x.x.x.x的服务器上 环境准备 第一步&#xff0c;下载server-jre-8u202-linux-x64.tar.gz安装包。 登录地址&#xff1a;https://www.oracle.com/java/technologies/javase/javase8-archive-downloads.html下载server-jre-8u202-linux-x64.tar.gz…