《汇编语言》- 读书笔记 - 附注

《汇编语言》- 读书笔记 - 附注

  • 附注1:Intel 系列微处理器的3种工作模式
    • 1. 实模式
    • 2. 保护模式
      • 保护模式 与 实模式 的主要区别
        • 寻址能力
        • 内存保护
        • 特权级别
        • 任务管理和虚拟内存
      • 为何需要保护模式
      • 访问受保护资源
    • 3. 虚拟 8086 模式
    • 4. 长模式(Long Mode)
      • 64位CPU + 32位系统上运行32位应用
      • 64位CPU + 64位系统上运行32位应用
  • 附注2:补码
  • 附注3:汇编编译器(masm.exe)对jmp 的相关处理
    • 1. 向前转移
      • 1.1. 当 disp ∈ [-128, 127]
        • 书中描述
        • 上机验证:与书中描述并不符
      • 1.2. 当 disp ∈ [-32768, 32767]
        • 书中描述
        • 上机验证:与书中描述一至
    • 2. 向后转移
      • 2.1. 当 disp ∈ [-128, 127]
        • 书中描述
        • 上机验证:与书中描述并不符
      • 2.2. 当 disp ∈ [-32768, 32767]
        • 书中描述
        • 上机验证:与书中描述一至
    • 3. 总结
      • 向前跳转的处理
      • 向后跳转的处理
  • 附注4:用栈传递参数
    • C 语言示例
  • 附注5:公式证明
    • 分析:
      • 原理:分而治之各各击破
      • 公式分解
      • 证明过程
      • 伪代码分析验证

附注1:Intel 系列微处理器的3种工作模式

微机中常用的 Intel 系列微处理器的主要发展过程是:8080,8086/8088,80186,80286,80386,80486,Pentium,PentiumII,PentiumIII,Pentium4。

  • 8086 与 8088 微处理器的主要区别
    1. 数据总线宽度:8086具有16位外部数据总线,而8088为8位,这直接影响了数据传输的速度和效率。
    2. 指令队列:8086拥有6字节指令队列,相比8088的4字节,能更高效地预取指令。
    3. 系统兼容性:8088设计上偏向于与8位系统及外设兼容,更适合当时市场的需求。
    4. 性能:因总线宽度和指令队列的差异,8086通常提供更高的处理性能,而8088在某些应用场景下可能因系统集成的便利性和成本优势而被选用。
    5. 硬件接口:两者的控制信号和引脚配置有细微差别,需针对性设计硬件接口。

总体而言,8086是高性能选择,而8088则是成本效益与兼容性更好的方案。

1. 实模式

当Intel推出80286及后续的x86系列处理器时,为了确保向后兼容性,使这些新处理器能够运行为8086/8088设计的软件,它们在启动时都进入了实模式。
实模式是CPU在物理层面上按照类似于8086/8088处理器的工作方式来操作的一种模式。在实模式下:

  1. CPU地址总线访问限制在20位,因此寻址能力最大为1MB(即1,048,576字节)的物理地址空间。
    覆盖从00000HFFFFFH的地址范围。
  2. 使用16位寄存器。段寄存器与偏移地址组合形成20位地址,用于访问内存。
  3. 没有现代处理器的页表和分页机制,内存管理较为简单。
  4. 中断和异常处理机制较为基础,不支持像保护模式下的高级特性。
  5. 操作系统直接与硬件交互,没有现代操作系统中的硬件抽象层。

实模式的存在使得老的16位操作系统应用程序可以在更先进的32位64位处理器上运行,无需修改。

补充:VMware, VirtualBox 等虚拟机也可以提供一个高度仿真的环境来运行DOS,但这种模拟是在软件层次完成的,而非物理CPU直接进入或模拟8086的工作模式。
虚拟化技术的意义之一就在于,它允许宿主机的CPU保持在高效的保护模式下运行现代操作系统,同时在虚拟机内部模拟出一个包含8086/8088兼容环境的平台,来运行16位的操作系统如DOS以及为其设计的软件。这种方式不仅保留了对旧软件的兼容性,还确保了宿主机系统的安全性和资源的有效隔离,同时也充分利用了现代硬件的高性能特性。因此,用户可以在享受最新技术带来的便利的同时,无缝兼容和利用遗留的软件资源,这是虚拟化技术在保持向后兼容性方面的一个重要贡献。

2. 保护模式

保护模式 与 实模式 的主要区别

寻址能力
  • 实模式: 受限于16位段寄存器偏移量的方式,地址总线为20,仅能访问最多1MB内存。
  • 保护模式: 利用更先进的地址转换机制,如分段和分页,可支持远大于1MB的地址空间,如32位处理器可达4GB
内存保护
  • 实模式: 内存访问无任何保护措施,任何程序均可自由读写内存的任何位置,容易引发系统崩溃或安全漏洞。
  • 保护模式: 通过引入内存管理和权限控制,确保程序只能访问其权限内的内存区域,有效隔离了不同程序及用户程序与操作系统内核,大大增强了系统的稳定性和安全性。
特权级别
  • 实模式: 没有权限概念,用户应用与操作系统平起平坐。(用户程序出点什么错很轻松就能把系统一起搞死)
  • 保护模式: 设置了多个特权级别(如Ring 0至Ring 3),操作系统核心运行在最高权限级别,用户程序则在较低级别,这样的设计进一步限制了用户程序对系统资源的直接访问,提升了安全性。
任务管理和虚拟内存
  • 实模式: 系统没有内建的任务管理和虚拟内存机制。但可以通过中断、常驻内存程序(TSR)、直接硬件操作、手动内存管理以及程序间合作等不同的策略,来实现类似多个应用程序协同工作的功能。
  • 保护模式: 支持多任务和虚拟内存,操作系统通过分页机制将虚拟地址转换为物理地址,使得每个程序享有独立的虚拟地址空间,感觉像是独占了整个内存空间,而实际物理内存的分配和管理则由操作系统统一处理,提高了资源利用效率和程序的独立性。

为何需要保护模式

保护模式的引入主要是为了解决实模式下存在的安全、稳定性和资源管理效率等问题。它通过提供内存保护、特权级划分、虚拟地址空间和多任务支持,不仅保护了系统和用户数据免受恶意或错误程序的侵害,还促进了操作系统设计的复杂性和灵活性,支撑了现代计算系统的发展需求。

访问受保护资源

在保护模式下,应用程序无法直接访问受保护的内存或硬件资源。为了与这些资源交互,应用程序需通过操作系统提供的API进行请求。API作为安全的中介,会对请求进行权限验证,并以一种控制和监管的方式执行相应操作,确保了所有访问都在安全和有序的框架内进行,进一步巩固了保护模式的核心优势。

3. 虚拟 8086 模式

虚拟8086模式是一种特殊的处理器运行模式,它是在保护模式下模拟实模式8086处理器行为的一种机制。这一模式的意义在于,它允许在较新的操作系统和硬件平台上,仍能运行为16位实模式设计的老式应用程序和DOS程序。在虚拟8086模式下,尽管处理器实际上处于保护模式,但它能够为每个任务模拟出一个隔离的16位环境,从而保持与早期软件的兼容性。

然而进入64位时代后,虽然CPU硬件还在继续支持虚拟 8086 模式
但64位版本的Windows操作系统已经不直接支持16位应用程序了。这是因为64位Windows操作系统主要运行在长模式(Long Mode)下,该模式不兼容实模式虚拟8086模式,从而无法直接运行16位代码。

64位系统下运行16位应用的推荐方案是:通过虚拟化技术,在安装有16、32位操作系统的虚拟机中运行这些旧程序。

4. 长模式(Long Mode)

在支持64位运算的处理器(如AMD的Opteron和Intel的Xeon处理器)中引入,它允许CPU使用更大的地址空间(理论上可达16 EB),并扩展了通用寄存器的大小到64位,同时保持了保护模式下的大部分特性。
长模式保护模式64位计算环境中的扩展和升级自然演进,而不是替代。

64位CPU + 32位系统上运行32位应用

  • 32位操作系统
    32位操作系统根据32位架构设计规范来构建和运行,它有一套固定的规则和接口来与硬件交互,这些规则不依赖于CPU的具体位数,而是基于32位系统的标准。
    当这套操作系统部署在64位CPU上时,CPU能够识别它正在与一个32位操作系统交互,并相应地调整其行为来兼容和支持该操作系统。

    64位CPU通过特定的保护模式(所谓的兼容模式),向下兼容32位的指令集和寻址方式,从而确保32位操作系统可以正常运行。这意味着CPU不仅知道它正在服务于一个32位系统,而且还会主动调整其工作模式和功能,以满足该系统的需求。
    尽管CPU具有更高级的功能(如64位运算和寻址),但在运行32位系统时,它会限制这些功能的使用,以维持与32位系统软件的兼容性。

  • 32位应用程序
    32位应用程序在由操作系统营造的32位兼容环境中无障碍地运行,对底层硬件的实际位宽并不知情也不需关心。

64位CPU + 64位系统上运行32位应用

跑在64位CPU上的64位操作系统上运行32位应用程序时,并不需要完全离开长模式。相反,CPU和操作系统共同协作,创造一个仿真的32位环境给这个应用程序,使其认为自己正运行在一个32位系统上。这包括限制地址空间到32位范围、使用32位指令集等,而所有这一切都是在长模式的框架内实现的。简而言之,CPU确实是依然工作在长模式下,只是通过特定的子模式和操作系统的辅助,巧妙地“伪装”出一个适合32位程序执行的环境。

补充: 64位CPU在运行32位操作系统或应用程序时,会切换到一个特定保护模式下,这个模式全面兼容32位环境。这意味着CPU会限制其自身,以模拟32位处理器的行为,包括使用32位地址空间、寄存器以及指令集,从而确保与32位操作系统和应用程序的完全兼容。尽管CPU本身是64位的,但在这种模式下,它能够有效地回退到32位时代的技术要求,保证软件的正常运行。这种能力让64位系统具备了良好的向后兼容性,使得用户可以在现代硬件上继续运行较老的32位软件资源。

附注2:补码

这个之前写过,直接看 笑虾:学习笔记:原码, 反码, 补码

附注3:汇编编译器(masm.exe)对jmp 的相关处理

1. 向前转移

s:  :::
jmp s ( jmp short s, jmp near ptr s, jmp far ptr s )

编译器中有一个地址计数器(AC ) ,
编译器在编译程序过程中,每读到一个字节AC+1
当编译器遇到一些伪操作的时候,也会根据具体情况使AC增加,如dbdw 等。
在向前转移时,编译器可以在读到标号s 后记下 AC 的值 as
在读到jmp … s 后记下AC的值aj
然后用as – aj 算出位移量disp

1.1. 当 disp ∈ [-128, 127]

书中描述

此时无论是 jmp s, jmp short s, jmp near ptr s, jmp far ptr s 都会转为 jmp short s

assume cs:code
code segment
s:  jmp sjmp short sjmp near ptr sjmp far ptr s
code ends
end s
上机验证:与书中描述并不符

Debug查看,显示结果与书中描述并不一样。
简单的说 jmp s 转成了 jmp short s ,而 jmp near ptr sjmp far ptr s 原样不变。
可能与作者当时的 masm 版本有关???疑问中。。。

assume cs:code
code segment
s:  jmp s			; 076E:0000	EBFE		JMP 0000		此时IP已是2,所以位移-2(补码 FE)jmp short s		; 076E:0002	EBFC		JMP 0000		此时IP已是4,所以位移-4(补码 FC)jmp near ptr s  ; 076E:0004	E9F9FF		JMP	0000		此时IP已是7,所以位移-7(补码 FFF9)jmp far ptr s   ; 076E:0007	EA00006E07	JMP	076E:0000	far 是绝对地址跳转。段地址:偏移地址
code ends
end s

在这里插入图片描述

使用指令机器码机器码对应指令
jmp sEB disp(disp 1字节,共占2字节)jmp short s
jmp short sEB disp(disp 1字节,共占2字节)jmp short s
jmp near ptr sE9 disp(disp 2字节,共占3字节)jmp near ptr s
jmp far ptr sEA disp:disp(disp 4字节,共占5字节)jmp far ptr s

1.2. 当 disp ∈ [-32768, 32767]

书中描述
指令结果占空间
jmp short s编译报错
jmp s
jmp near ptr s
生成 jmp near ptr s 对应机器码 E9 dispdisp 2字节,共占3字节
jmp far ptr s生成 jmp far ptr s 对应机器码 EA disp:disp2个disp 4字节,共占5字节

编译以下程序 jmp short s 将产生编译错误,去掉后再编译即可成功。

assume cs:code
code segment
s:  db 100 dup (0b8h, 0, 0)     	; 重复 (0b8h , 0 , 0 ) 100次,共占 300 字节jmp short s       				; disp 已经超出 short 的偏移范围,将产生编译错误jmp sjmp near ptr sjmp far ptr s
code ends
end s
上机验证:与书中描述一至

用 Debug 进行反汇编查看如下图:
在这里插入图片描述

使用指令机器码机器码对应指令
jmp short s产生编译错误
jmp sE9 disp(disp 2字节,共占3字节)jmp near ptr s
jmp near ptr sE9 disp(disp 2字节,共占3字节)jmp near ptr s
jmp far ptr sEA disp:disp(两个disp 各2字节,共占5字节)jmp far ptr s

2. 向后转移

jmp s ( jmp short s, jmp near ptr s, jmp far ptr s )::
s:  :

在这种情况下,编译器先读到 jmp ... s 指令。由于还没读到标号s,不知道它的 AC值,也就无法算出位移量 disp 的大小。
此时,编译器将记下当前 jmp 指令的位置和 AC 值 aj。并生成相应机器码和对应数量的占位符

指令机器码预留
jmp short sEB nop预留1字节空间,存放8位 disp
jmp sEB nop nop预留2字节空间,存放16位 disp
jmp near ptr sEB nop nop预留2字节空间,存放16位 disp
jmp far ptr sEB nop nop nop nop预留4字节空间,存放段地址偏移地址

然后编译器继续工作,向后读到 标号s 时,记下 AC 的值as,并计算出转移的位移量: disp = as-aj
此时,编译器作如下处理:

2.1. 当 disp ∈ [-128, 127]

书中描述

填充占位:
对于jmp sjmp near ptr s,机器码 EB disp 后还有1条 nop指令
对于 jmp far ptr s 格式,在机器码 EB disp 后还有3条 nop 指令。

指令机器码预留空间
jmp short sEB disp 预留1字节空间,存放8位 disp
jmp sEB disp nop预留2字节空间,存放16位 disp
jmp near ptr sE8 disp nop预留2字节空间,存放16位 disp
jmp far ptr sE8 disp nop nop nop预留4字节空间,存放段地址偏移地址

编译,连接以下程序,用 Debug 进行反汇编查看。

assume cs:code
code segment
begin:jmp short sjmp sjmp near ptr sjmp far ptr s
s:        mov ax,0
code  ends
end  begin
上机验证:与书中描述并不符

Debug查看效果如下图:
在这里插入图片描述
这是 jmp s 最终生成的是 EB08 与书描述的预留 EB nop nop 不符,可能原因有2:

  1. 书上说错了。
  2. 编译器对结果进行了优化,去掉了多出来的那个 nop

(继续向下看,有新发现)

2.2. 当 disp ∈ [-32768, 32767]

书中描述

编译以下程序将产生编译错误,错误是由 jmp short s 引起的,去掉后再编译就可以通过。

assume cs:code
code segment
begin:jmp short s				; disp 已经超出 short 的偏移范围,将产生编译错误jmp sjmp near ptr sjmp far ptr sdb 100 dup (0b8h, 0, 0)	; 重复 (0b8h , 0 , 0 ) 100次,共占 300 字节
s:  mov ax,2
code ends
end begin

用 Debug 进行反汇编查看:

上机验证:与书中描述一至

在这里插入图片描述
从这里可以看到 jmp s 最终生成的是 E93401 与书描述的预留两个 nop 相符,
我们可以合理推测当 disp ∈ [-128, 127] 时,编译器对最终结果时行了优化,去掉了多余的 nop

3. 总结

向前跳转的处理

  • 位移量计算:在向前跳转时,编译器能够直接计算出从跳转指令目标标号的位移量(disp),因为它已经遇到了目标标号并记录了其位置。此偏移量基于代码段内的地址差值。

  • 位移量超出短跳转范围:若计算得出的位移量超出了短跳转所能覆盖的范围(即不在[-128, 127]内),编译器会生成适合的跳转指令。

    • jmp short s会导致编译错误,
    • jmp near ptr s,会生成E9 disp格式,其中disp是位移量的补码形式,占2字节,支持更广泛的地址空间。
    • jmp far ptr s,则涉及段间跳转,生成EA disp:disp格式,包含段地址2字节,偏移地址2字节。

向后跳转的处理

  • 占位符使用
    由于编译器在遇到跳转指令时尚未读到目标标号,它会先生成跳转指令的占位符,并预留足够的空间以待后续填写实际的位移量。对于不同的jmp类型,预留空间大小不同。

  • 位移量确定与回填

    • 一旦所有指令长度确定,编译器会回过头来计算实际的位移量。对于向后的jmp near ptr s,如果计算出的位移在短跳范围内,尽管理论上可以编码为EB disp,但根据指令原意,通常会保持为E9 disp格式,确保代码兼容、稳定和语义的一至性。
    • 对于jmp far ptr s,无论位移大小,最终都会根据实际计算填写完整的远跳转指令,即EA disp:disp,确保段间正确跳转。

通过这种方式,无论是向前还是向后跳转,编译器都能确保生成正确的机器码,满足程序执行时的跳转需求。

附注4:用栈传递参数

调用者将参数入栈,子程序从栈中取参数。
栈操作的基本单位是(2字节)。
所以调用者压栈n个参数,子程序使用完后,返回时就 ret 2n
指令 ret n 的含义用汇编语法描述为:

pop ip
add sp, n
assume cs:code
code segmentmov ax,1	; 参数 bpush axmov ax,3	; 参数 apush axcall difcubemov ax,4c00hint 21h;说明: 计算(a-b)^3,a、b 为字型数据 word
;参数: 进入子程序时,栈顶存放IP,后面依次存放a、b
;结果: (ds:ax)=(a-b)^3difcube:push bpmov bp,spmov ax,[bp+4]	; 将栈中a的值送入ax中sub ax,[bp+6]	; 减栈中b的值mov bp,axmul bpmul bppop bpret 4code ends
end

C 语言示例

通过一个 C 语言程序编译后的汇编语言程序,看一下栈在参数传递中的应用。
在C语言中,局部变量也在栈中存储。

void add (int,int,int);main()
{int a=1;int b=2;int c=0;add(a,b,c);c++;printf("c = %d", c);
}void add(int a,int b,int c)
{c=a+b;
}

不知道书上是用什么编译的,我这里用 tcc -S demo.c 编译的和书上不太一样:
(略掉部分无关代码)

_TEXT	segment	byte public 'CODE'
_main	proc	nearpush bp					; 备份调用者栈帧基址mov	bp,sp				; 创建 main 当前栈帧sub	sp,2				; 开辟 2 字节局部空间push	si				; 备份寄存器push	dimov	di,1				; int a=1;mov	word ptr [bp-2],2	; int b=2;xor	si,si				; int c=0;push	si				; 参数 c 压栈push	word ptr [bp-2]	; 参数 b 压栈push	di				; 参数 a 压栈call	near ptr _add	; add(a,b,c); add	sp,6					; add 返回后清掉栈中的3个参数inc	si					; c++;pop	di					; 还原寄存器pop	simov	sp,bp				; 销毁 main 当前栈帧pop	bp					; 恢复调用者栈帧基址ret						; 返回
_main	endp_add	proc	nearpush bp					; 备份调用者栈帧基址mov	bp,sp				; 创建 add 当前栈帧mov	ax,word ptr [bp+4]add	ax,word ptr [bp+6]mov	word ptr [bp+8],axpop	bp					; 恢复调用者 main 栈帧基址ret						; 返回
_add	endp
_TEXT	endspublic	_main			; 声明为公共public	_add			; 声明为公共
end

附注5:公式证明

证明公式:X/N = int(H/N) * 65536 + [ rem(H/N) * 65536 + L ] / N 不会溢出

分析:

原理:分而治之各各击破

  1. 一个32位数除以16位数的结果,对于16位寄存器并不是总能装下。如:
1111 1111 1111 1111 1111 1111 1111 1111		; 320111 1111 1111 1111 1111 1111 1111 1111		; 除以2相当于右移一位。;31 位的结果 16 位寄存器肯定是存不下的
  1. 但是如果把高位,低位拆分开来处理,就是16位对16位了,无论如何都能存下。
1111 1111 1111 1111		; 161111 1111 1111 1111		; 除以 1 够狠了吧,16位寄存器还是能装下
  1. 最后再将高位,低位的结果各自放对应位置上就组成了正确的结果。

公式分解

让我们逐步分析这个公式的各个部分,以及它是如何避免溢出的。

公式为:X/N = int(H/N) * 65536 + [ rem(H/N) * 65536 + L ] / N

  • (X) 是32位的被除数,范围是[0, FFFFFFFF](即0 ~ 4,294,967,295)。
  • (N) 是16位的除数,范围是[0, FFFF](即0 ~ 65535)。
  • (H) 是(X)的高16位,范围是[0, FFFF]。
  • (L) 是(X)的低16位,范围也是[0, FFFF]。

证明过程

  1. 处理高16位(H)int(H/N) * 65536
    int(H/N):这部分计算H除以N的整数部分,结果范围是[0, 65535],
    因为H最大为65535,除以最大除数65535时的整数商 =1
    1 再乘以65536(即2^16)后,结果范围 [0, 65536] 在 [0, 4,294,967,295] 之内,不会溢出32位。

  2. 处理高16位余数 + 低16位(L)[ rem(H/N) * 65536 + L ] / N
    低16位要和高16位余数合并处理,[ rem(H/N) * 65536 + L ] 这部分最大 [0, 4,294,901,759]
    仍然小于(2^324,294,967,296) 属于安全范围。

    计算余数取值范围10进制表示16进制表示
    rem(H/N)[0, N-1]= [0, 65535-1]
    = [0, 65534]
    [0, FFFE]
    rem(H/N) * 65536[0, (N-1) * 65536]= [0, 65534 * 65536]
    = [0, 4,294,836,224]
    [0, FFFE 0000]
    rem(H/N) * 65536 + L[0, (N-1) * 65536 + 65535]= [0, 4,294,836,224 + 65535]
    = [0, 4,294,901,759]
    [0,FFFE FFFF]
  • 余数性质之一:余数 = 被除数 - 除数 × 商。根据性质可知余数取值范围:0 <= 余数 < 除数 )
  • 这里公式中的乘以65536,并不需要真的在16位寄存器中去乘。
    比如将结果放到代表高16位dx中,就相当于 x 65536 了(也就是左16位,从低16位挪到高16位去了)。

伪代码分析验证

	; 先处理32位被除数的高16位,【商】和【余数】一次 div 就到手mov ax,0ffffh	; dword 被除数的高 16位mov dx,0		; 32位被除数,高16位放到 ax 去了,dx要补 0mov cx,2		; 16位除数div cx			; 执行后,对应公式中这两段:; AX = int(H/N) * 65536 =16位商; DX = rem(H/N) * 65536 =16位余数push ax			; 暂存高 16 位的商; 再处理32位被除数低16; 因为【高16位余数】已经在 dx 里(相当于已经 x 65536)mov ax,0ffffh	; 现在只要将低16位装进 ax 即完成了 [16位余数 + L ]div cx			; 执行后,对应公式中这两段:; AX = [16位余数 + L ] / N =16位商; DX = [16位余数 + L ] / N =16位余数; 调整一下位置即可得到最终结果mov cx,dx		; 余数归位pop dx			;16 位的商归位
寄存器被除数除数余数
ax低16位低16位
dx高16位高16位
cx16位16位

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

R语言学习—6—多元相关与回归分析

1、引子 xc(171,175,159,155,152,158,154,164,168,166,159,164) #身高 yc(57,64,41,38,35,44,41,51,57,49,47,46) #体重 par(marc(5,4,2,1)) #设定图距离画布边缘的距离&#xff1a;下5&#xff0c;左4&#xff0c;上2&#xff0c;右1 plot(x,y) 2、相关…

【C语言实现贪吃蛇】(内含源码)

前言&#xff1a;首先在实现贪吃蛇小游戏之前&#xff0c;我们要先了解Win32 API的有关知识 1.Win32 API Windows这个多作业系统除了协调应用程序的执行、分配内存、管理资源之外&#xff0c;它同时也是一个很大的服务中心&#xff0c;调佣这个中心的各种服务&#xff08;每一…

电话号码的字母组合 【C++】【力扣刷题】

解题思路&#xff1a; 以第一个为例,digits “23”&#xff0c;表明从电话号码的按键中选取2和3这两个字符&#xff0c;然后去寻找它们各自所对应的字母&#xff0c;这里每一个数字字符所对应的字母的不同&#xff0c;0对应的是空字符&#xff0c;而1的话题目中讲到是不对应任…

【webrtc】MessageHandler 6: 基于线程的消息处理:StunRequest实现包发送和超时重传

G:\CDN\rtcCli\m98\src\p2p\base\stun_request.cc使用OnMessage 实现包的发送和包的超时重传StunRequest 一个StunRequest 代表是一个独立的请求的发送STUN消息 要不是发送前构造好的,要不就是按照需要构建的使用StunRequestManager: 每一个STUNRequest 携带一个交互id 写入m…

CUDA和显卡驱动

1.安装显卡驱动 https://www.nvidia.com/download/index.aspx?langen-us 由于我的显卡是RTX4060&#xff0c;因此先选择RTX40系列&#xff0c;然后选择RTX4060&#xff0c;进行安装 2.查看显卡对应的CUDA CUDA安装地址&#xff1a;https://developer.nvidia.com/cuda-toolk…

一文带你了解MySQL的索引分类

文章目录 ☃️分类☃️演示图☃️思考☃️总结 欢迎来到 请回答1024 的博客 &#x1f353;&#x1f353;&#x1f353;欢迎来到 请回答1024的博客 关于博主&#xff1a; 我是 请回答1024&#xff0c;一个追求数学与计算的边界、时间与空间的平衡&#xff0c;0与1的延伸的后端开…

【YOLO 系列】基于YOLO V8的27类蔬菜水果检测识别系统【python源码+Pyqt5界面+数据集+训练代码】

前言&#xff1a; 蔬菜水果的种类繁多&#xff0c;是人们日常生活中不可或缺的一部分。然而&#xff0c;在蔬菜水果的生产、销售和储存过程中&#xff0c;往往面临着诸多挑战&#xff0c;如品质检测、分类等问题。为了提高蔬菜水果行业的生产效率和产品质量&#xff0c;我们提…

【redis】redix在Linux下的环境配置和redis的全局命令

˃͈꒵˂͈꒱ write in front ꒰˃͈꒵˂͈꒱ ʕ̯•͡˔•̯᷅ʔ大家好&#xff0c;我是xiaoxie.希望你看完之后,有不足之处请多多谅解&#xff0c;让我们一起共同进步૮₍❀ᴗ͈ . ᴗ͈ აxiaoxieʕ̯•͡˔•̯᷅ʔ—CSDN博客 本文由xiaoxieʕ̯•͡˔•̯᷅ʔ 原创 CSDN 如…

学习Rust的第25天:Rust中的mkdir

为了实现重建 GNU 核心实用程序的崇高目标&#xff0c;今天的工具是 mkdir 我们用来在 Linux 上创建目录的工具。让我们开始吧。 Pseudo Code 伪代码 args command_line_arguments remove the first element of the args vector if args.length 0 {print error_message } els…

STM32学习和实践笔记(24):PWM输出实验:呼吸灯

本实验所要实现的功能是&#xff1a;通过TIM3的CH1输出一个PWM信号&#xff0c;控制D7指示 灯由暗变亮&#xff0c;再由亮变暗&#xff0c;类似于人的呼吸。程序框架如下&#xff1a; &#xff08;1&#xff09;初始化PC6管脚为PWM输出功能 &#xff08;2&#xff09;PWM输出…

RAG应用全流程

RAG全流程 前提训练一个语义模型&#xff08;高精度&#xff0c;低精度&#xff09;训练一个大模型一个知识库一个精度高知识向量库&#xff08;知识分割后输入高精度语义模型得到&#xff09;一个精度低知识向量库&#xff08;知识分割后输入低精度语义模型得到&#xff09; 应…

基于寄存器的STM32操作流程

寄存器点灯 寄存器操作STM32的好处是不需要依靠外部文件&#xff0c;自由度更高&#xff0c;更为底层&#xff0c;但也更加繁杂。 通过寄存器点灯&#xff0c;需要按照电路结构与手册配置寄存器&#xff1a; 电路结构如下&#xff1a;可知需配置的GPIO为GPIOB5与GPIOE5。 在…

TLV61070A具有0.5V超低输入电压的2.5A同步整流升压转换器适合超级电容供电

前言 最大特点是输入电压可低至0.5V&#xff08;启动时的最小输入电压为 1.3V&#xff09;&#xff0c;适合作为超级电容的升压转换 特性 输入电压范围&#xff1a;0.5V 至 5.5V 启动时的最小输入电压为 1.3V 输出电压设置范围&#xff1a;2.2V 至 5.5V 两个 69mΩ (LS)/89m…

gitlab设置保护分支

gitlab设置保护分支方法 进入代码仓库首页&#xff0c;找到settings下的repository并点击进入 找到Protected Branches 下的Exoand按钮&#xff0c;并点击展开 可以看到已经存在默认的保护分支&#xff0c;通常是master/main分支&#xff0c;也可以添加新的保护分支 新建保护分…

Debian操作系统的常用指令介绍

Debian是一个流行的Linux操作系统&#xff0c;以其稳定性和安全性而闻名。对于Debian用户来说&#xff0c;掌握一些基本的命令行指令是非常重要的&#xff0c;因为它们可以帮助你更高效地管理系统。在这篇博客中&#xff0c;我们将介绍一些在Debian系统中常用的指令及其功能。 …

13.1 QQ邮箱

1. 邮箱发送 2. 准备工作 3. 整合SpringBoot 3.1 配置 依赖引入 <!-- 邮件服务--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-mail</artifactId></dependency>application.…

不讲武德,能怼哭你的Chatgpt

这几天逛网站的时候突然发现个新玩意儿&#xff0c;叫做Chatgpt Onima&#xff0c;乍一看&#xff0c;哦原来是Chatgpt 但是&#xff0c;Onima是什么东东&#xff1f;它是我见过最狂的AI Onima 我礼貌的问了一句&#xff1a;你在干嘛&#xff1f; 结果它回复 不知这个攻击性…

通义灵码:智能编码的革命性助手

通义灵码是由阿里云推出的一款基于通义大模型的智能编码辅助工具&#xff0c;它通过先进的人工智能技术&#xff0c;为开发者提供了一系列的智能编码功能&#xff0c;极大地提升了编码效率和质量。以下是通义灵码的一些核心功能和应用案例。 核心功能 代码智能生成 通义灵码…

你知道JSON.stringify()实现拷贝有什么问题吗?

在说 JSON.stringify() 深拷贝之前&#xff0c;我们先说一说深拷贝和浅拷贝的事情吧。 目录 1 为什么要做深拷贝&#xff1f; 2 哪些做法算浅拷贝&#xff1f; 2.1 直接变量赋值 2.2 Object.assign 浅拷贝 3 哪些做法算深拷贝 &#xff1f; 3.1 JSON.stringify() 3.2 …

SpringBoot实现图片上传(个人头像的修改)

SpringBootlayui实现个人信息头像的更改 该文章适合对SpringBoot&#xff0c;Thymeleaf&#xff0c;layui入门的小伙伴 废话不多说&#xff0c;直接上干货 Springbootlayui实现头像更换 前端公共部分代码 HTML页面代码 <div class"layui-card-header" style&quo…