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

5. ARM_指令集

概述

分类

汇编中的符号:

  • 指令:能够编译生成一条32位机器码,并且能被处理器识别和执行
  • 伪指令:本身不是指令,编译器可以将其替换成若干条指令
  • 伪操作:不会生成指令,只是在编译阶段告诉编译器怎么编译

对于不同的CPU,指令和伪指令不同。因为指令和伪指令都由CPU来直接识别。

对于不同的编译器,伪操作不同,因为伪操作是控制编译器编译的。

ARM指令集的分类:

  • 数据处理指令:进行数学运算、逻辑运算
  • 跳转指令:实现程序的跳转,本质就是修改了PC寄存器
  • Load/Store指令:访问(读写)内存
  • 状态寄存器传送指令:用于访问(读写)CPSR寄存器
  • 软中断指令:触发软中断
  • 协处理器指令:操作协处理器的指令

其中数据处理指令、跳转指令、Load/Store指令是通用指令,任何一个CPU都具有该指令。并且这些指令在C语言中都有对应的语句。

指令格式:

汇编语言不区分大小写,但是不要出现一个指令里面既有大写也有小写。比如:可以写MOV或mov,但不要写成Mov 

立即数

什么是立即数:

立即数就是能够写在指令后面的数,例如 " MOV R0 #1 " 这个#1当中的数就是立即数。

立即数的本质就是包含在指令当中的数,属于指令的一部分。

注意:立即数不能写到32位,因为汇编指令转为机器码后是32位,这32位中有几位用来表示指令含义和寄存器含义,因此数据位并没有32位。

立即数与变量的区别:

立即数是指令的一部分,在使用立即数赋值时,CPU直接从机器码中就可以获取数据,不需要从内存中读取相应的值。

变量是存放在内存中,通过变量赋值,CPU必须先从内存中取出,再进行赋值。

32位非立即数却可赋值的情况:

当我们使用MOV R0, #0xFFFFFFFF这个指令时,0xFFFFFFFF是一个32位的数,一定不是一个立即数,但最终编译未出错。具体的编译器处理结果如下:

在这里,编译器将MOV R0, #0xFFFFFFFF 替换成了 MVN R0,#00000000,这个MVN的指令就是CPU可以识别执行的指令,原先的MOV就被称作伪指令。

条件码

条件码就是一个条件,当条件满足时执行这个操作,条件不满足时不执行该操作。

大多数的指令后都可以添加条件,具体示例见"跳转指令" - "2、B"

在使用条件码时,需要先使用CMP进行比较,之后再使用条件码进行条件执行。

条件码如下:

数据处理指令

1、数据搬移指令

1.1 MOV

将指定的数据搬移(赋值)到指定的寄存器中。

格式:

MOV 目标寄存器, 源操作数

目标寄存器: 可以为通用寄存器R0~R12,也可以是PC

源操作数:可以是立即数,也可以是寄存器

示例:

将立即数3搬移到寄存器R1,即:令R1=3

MOV R1, #3     @ #3代表十进制的3,#0x3代表十六进制的3

将R2寄存器的值搬移到寄存器R1,即:令R1=R2

MOV R1, R2

1.2 MVN

将指定的数据按位取反后,搬移(赋值)到指定的寄存器中。

格式: 

MVN 目标寄存器, 源操作数

示例:

将立即数0xFF取反后的值搬移到寄存器R1,即:令R1 = ~0xFF。

注意:这时R1存放的值为0xFFFFFF00,而不是0,因为ARM的寄存器是32位,所以在ARM寄存器中0xFF的存放值是0x000000FF,因此按位取反后为0xFFFFFF00,而不是0。

MVN R1 #0xFF

2、数值运算指令

一般格式:<操作码> <目标寄存器> <第一操作寄存器> <第二操作数>

  • 操作码:执行什么操作,如加减乘除
  • 目标寄存器:用于存放运算结果
  • 第一操作寄存器:必须是一个寄存器,是参与运算的数。
  • 第二操作数:可以是寄存器,也可以是立即数,这也是参与运算的数。

2.1 ADD

加法指令,将指定的两个数进行相加,结果存入指定的寄存器。

格式:

ADD 目标寄存器, 源寄存器, 源操作数

目标寄存器: 可以为通用寄存器R0~R12,也可以是PC

源寄存器:只能是寄存器

源操作数:可以是立即数,也可以是寄存器

示例:

已知R2=5,R3=3,实现R1=R2+R3

MOV R2,#5
MOV R3,#3
ADD R1,R2,R3 @ R1 = R2 + R3

已知R2=5,实现R1=R2+5

MOV R2,#5
ADD R1,R2,#5 @ R1 = R2 + 5
注意:这里不能写成ADD R1,#5,R2 对应ADD只有最后一个为源操作数

2.2 ADC

带进位的加法指令,将指定的两个数进行相加,并且会加上CPSR的进位。 

ADC实现64位运算:

实现:

0x00000001 00000002 + 0x00000003 00000004

思路:

将第一个数的低位和高位分别存放在R1、R2;将第二个数的低位和高位分别存放在R3、R4;将结果的低位和高位分别存放在R5、R6

运算代码:

MOV R1,0x00000002
MOV R2,0x00000001
MOV R3,0x00000004
MOV R4,0x00000003

ADDS R5,R1,R3 @将两个低位相加并允许设置CPSR
ADC R6,R2,R4  @将两个高位相加并加上CPSR的C位(溢出进位)

ADCS实现128位运算:

实现:

0x00000001 00000002 00000003 00000004 + 0x00000005 00000006 00000007 00000008

运算代码:

MOV R1,0x00000004
MOV R2,0x00000003
MOV R3,0x00000002
MOV R4,0x00000001

MOV R5,0x00000008
MOV R6,0x00000007
MOV R7,0x00000006
MOV R8,0x00000005

ADDS R9,R1,R5     @将两个低位相加并允许设置CPSR
ADCS R10,R2,R6    @将两个高位相加并加上CPSR的C位(溢出进位)并允许设置CPSR
ADCS R11,R3,R7    @将两个高位相加并加上CPSR的C位(溢出进位)并允许设置CPSR
ADC  R12,R4,R8    @将两个高位相加并加上CPSR的C位(溢出进位)

2.3 SUB

减法指令,将指定的两个数进行相减,结果存入指定的寄存器。

该指令只能实现 "寄存器 - 寄存器" 或者 "寄存器 - 立即数" 但实现不了 "立即数 - 寄存器"

格式:

SUB 目标寄存器, 源寄存器, 源操作数

目标寄存器: 可以为通用寄存器R0~R12,也可以是PC

源寄存器:只能是寄存器

源操作数:可以是立即数,也可以是寄存器

注意:SUB减法中有顺序,源寄存器存放的是被减数,源操作数存放的是减数。

示例:

已知R2=5,R3=3,实现R1=R2-R3

MOV R2,#5
MOV R3,#3
SUB R1,R2,R3 @ R1 = R2 - R3

已知R2=5,实现R1=R2-5

MOV R2,#5
SUB R1,R2,#5 @ R1 = R2 - 5
注意:这里不能写成SUB R1,#5,R2 对应SUB只有最后一个为源操作数

2.4 SBC

带借位的减法指令,将指定的两个数进行相减,并且会减去CPSR的借位(C位取反)。

SBC实现64位运算:

实现:

0x00000002 00000001 - 0x00000001 00000002

运算代码:

MOV R1,0x00000001
MOV R2,0x00000002
MOV R3,0x00000002
MOV R4,0x00000001

SUBS R5,R1,R4 @将两个低位相减并允许设置CPSR
SBC R6,R2,R5  @将两个高位相减并减去CPSR的C位取反(借位)

2.5 RSB

逆向减法指令,将指定的两个数进行相减,结果存入指定的寄存器。

该指令通过被减数与减数调换,解决了SUB不能实现"立即数 - 寄存器"的问题。

格式:

RSB 目标寄存器, 源寄存器, 源操作数

目标寄存器: 可以为通用寄存器R0~R12,也可以是PC

源寄存器:只能是寄存器

源操作数:可以是立即数,也可以是寄存器

注意:RSB减法中有顺序,源寄存器存放的是减数,源操作数存放的是被减数。这与SUB相反。

示例:

实现R1 = 3 - R2。

RSB R1,R2,#3     @ R1 = 3 - R2

2.6 MUL

乘法指令,将指定的两个数进行相乘,结果存入指定的寄存器。

格式:

MUL 目标寄存器, 源寄存器, 源寄存器

目标寄存器: 可以为通用寄存器R0~R12,也可以是PC

源寄存器:只能是寄存器

注意:乘法指令中只能是两个寄存器相乘,不能使用立即数。

示例:

实现R1 = R2 * R3。

MUL R1,R2,R3     @ R1 = R2 * R3

3、位运算指令

3.1 AND

按位与指令,将指定的两个数进行按位与,结果存入指定的寄存器。

格式:

AND 目标寄存器, 源寄存器, 源操作数

目标寄存器: 可以为通用寄存器R0~R12,也可以是PC

源寄存器:只能是寄存器

源操作数:可以是立即数,也可以是寄存器

示例:

实现R1 = R2 & R3。

AND R1,R2,R3     @R1 = R2 & R3

3.2 ORR

按位或指令,将指定的两个数进行按位或,结果存入指定的寄存器。

格式:

ORR 目标寄存器, 源寄存器, 源操作数

目标寄存器: 可以为通用寄存器R0~R12,也可以是PC

源寄存器:只能是寄存器

源操作数:可以是立即数,也可以是寄存器

示例:

实现R1 = R2 | R3。

ORR R1,R2,R3     @R1 = R2 | R3

3.3 EOR

按位异或指令,将指定的两个数进行按位异或,结果存入指定的寄存器。

格式:

EOR 目标寄存器, 源寄存器, 源操作数

目标寄存器: 可以为通用寄存器R0~R12,也可以是PC

源寄存器:只能是寄存器

源操作数:可以是立即数,也可以是寄存器

示例:

实现R1 = R2 ^ R3。

EOR R1,R2,R3     @R1 = R2 ^ R3

3.4 LSL

左移指令,将指定的数左移指定的位,结果存入指定的寄存器。

格式:

LSL 目标寄存器, 源寄存器, 源操作数

目标寄存器: 可以为通用寄存器R0~R12,也可以是PC

源寄存器:只能是寄存器

源操作数:可以是立即数,也可以是寄存器

示例:

实现R1 = R2 << 3

方式1:寄存器法
MOV R3,#3
LSL R1,R2,R3 @R1 = R2 << 3

方式2:立即数法
LSL R1,R2,#3 @R1 = R2 << 3

 3.4 LSR

右移指令,将指定的数右移指定的位,结果存入指定的寄存器。

格式:

LSR 目标寄存器, 源寄存器, 源操作数

目标寄存器: 可以为通用寄存器R0~R12,也可以是PC

源寄存器:只能是寄存器

源操作数:可以是立即数,也可以是寄存器

示例:

实现R1 = R2 >> 3

方式1:寄存器法
MOV R3,#3
LSR R1,R2,R3 @R1 = R2 >> 3

方式2:立即数法
LSR R1,R2,#3 @R1 = R2 >> 3

3.5 BIC

位清零指令,将指定的位进行清零,结果存入指定的寄存器。

格式:

BIC 目标寄存器, 源寄存器, 源操作数

目标寄存器: 可以为通用寄存器R0~R12,也可以是PC

源寄存器:只能是寄存器

源操作数:可以是立即数,也可以是寄存器

注意:清零源操作数为1的位数,为0的位数不进行操作。但源寄存器的数值不进行改变

示例:

已知R2=0xFF,实现将R2低四位清零,并把结果写入到R1中,并且R2值不进行改变。

MOV R2,#0xFF
BIC R1,R2,#0x0F @0x0F的低四位为1,所以R2的低4位被清零,结果存入到R1
                @运算之后R1=0xF0,R2=0xFF(值不改变)

4、其他

4.1 格式扩展

格式扩展示例:

MOV R3, R0, LSL #3 ; 将R0的值左移3位后存入R3

格式扩展指令的看法:

首先这里面有MOV、LSR这两个指令,最前面的MOV是操作码,所以这个整体指令的功能是数据搬移功能,因此R0为第一操作数,LSL #3为第二操作数。

这里的LSL #3并不是一个指令,含义是左移3,操作的数据源就是前面的R0,返回的结果存放在前面的R3。因此最终功能是将R0的值左移3位后存入R3。

4.2 运算影响CPSR

指令后加S影响CPSR:

默认情况下,数据运算不会对CPSR的条件位产生影响。当指令加上后缀 "S" 后,即可影响CPSR

MOV R1,#3
SUB R2,R1,#5   @这计算是一个负数,但没有加S后缀,所以CPSR的N位不置1

MOV R1,#3
SUBS R2,R1,#5  @这计算是一个负数,加了S后缀,所以CPSR的N位置1

跳转指令

1、CMP指令

比较指令,比较两个寄存器值中的关系,比较的结果存储再CPSR的NZCV中。

CMP的本质就是一条减法指令(SUBS),只是没有将运算结果存入寄存器。

格式:

CMP 源寄存器1 源寄存器2

当源寄存器1 == 源寄存器2时,CPSR中的 Z = 1。

当源寄存器1 != 源寄存器2时,CPSR中的 Z = 0。

当源寄存器1 < 源寄存器2时,CPSR中的 C = 0。

当源寄存器1 <= 源寄存器2时,CPSR中的 C = 0 或者 Z = 1。

当源寄存器1 > 源寄存器2时,CPSR中的 C = 1 且 Z = 0(相等时也不产生借位)。

当源寄存器1 >= 源寄存器2时,CPSR中的 C = 1。

示例:

实现1~100求和,结果存入R2。 

2、B

跳转到指定的位置。跳转指令的本质就是修改PC指向。

注意:默认情况下LR不会自动保存下一条指令的地址,加上后缀 "L" 可以允许LR保存下一条指令的地址。

B称为不带返回的跳转指令,BL称为带返回的跳转指令。

格式:

B 条件码 标签

条件码可以不写,当不写时为无条件跳转,当写时为有条件跳转。 条件码如下:

示例:

1、无返回跳转到FUNC标号下的第一条指令位置。

当执行 " B FUNC " 之后,PC将指向 "MOV R6,#6" 并在下一次执行该指令。

2、有返回跳转到FUNC标号下的第一条指令位置,执行之后再返回。

当执行 " BL FUNC " 之后,PC将指向 "MOV R6,#6" 并且 LR存储了 "MOV R4,#4"的位置。

当执行 " MOV PC,LR "之后,PC将指向 "MOV R4,#4" 实现了返回。

 3、条件跳转,当R1=R2时,跳转到FUNC

当指向 "BEQ FUNC" 后,因为R1==R2不成立,所以不跳转,继续执行 "MOV R3,#3" 指令

4、跳转到自身

指令 "B ." 代表跳转到自身。

比如 "B ." 存在0x00000004处,执行完该指令后PC依旧指向0x00000004

Load/Store指令

1、单寄存器内存访问

1.1 STR

将指定寄存器中的数据存储到指定的内存地址中。

指令与存放数据的字节数:

  • STR   -- 4字节
  • STRH -- 2字节
  • STRB -- 1字节

格式:

STR 源寄存器 [目标寄存器]

目标寄存器中存放内存的地址

注意:源寄存器中数据为多少字节,那么目标寄存器存放的地址就得为多少的整数倍。

示例:

将R1的数据写入到R2存放的地址的位置。

这里R2存放的0x40000000为内存,这是因为地址映射时规定了这个地址是内存的起始地址。

这里R1的数据为0xFF000000是4字节,因此R2存放的地址应该为4的整数倍,所以如果R2的值为0x40000001,这就是错误的地址。

1.2 LDR

将指定的内存地址中的数据读取到指定寄存器中。

指令与取出数据的字节数:

  • LDR   -- 4字节
  • LDRH -- 2字节
  • LDRB -- 1字节

 格式:

LDR 目标寄存器 [源寄存器]

源寄存器存放的是所需取数据的数据地址。

示例:

将R2中存放的地址的数据读出来,存放到R3当中。 

该代码运行结束后,R3就存放了0xFF000000

2、多寄存器内存访问

2.1 STM

将多个寄存器中的数据存储到指定的内存地址中。

格式:

STM 目标地址 {源寄存器}

源寄存器:当寄存器连续时,使用 "-"链接;当寄存器不连续时,使用 ","分隔。

如 "STM R11,{R1-R4}" 这操作的时R1、R2、R3、R4

如 "STM R11,{R1,R4}" 这操作的时R1、R4

示例:

将R1~R4寄存器的值存放到以某个地址为起点的地址空间

这里的R11没有加 "[ ]",但它的数据含义代表地址

列表顺序并不是真正的存储顺序, 存储顺序一定是小编号的寄存器存低地址数据。

如上述STM指令写成 "STM R11,{R1,R4,R2,R3}",存储顺序依旧是和 "STM R11,{R1-R4}"一致,而不是0x40000020存放R1,0x40000024存放R4....

2.2 LDM

将指定的内存地址中的数据读取到多个寄存器中。

格式:

LDM 读取起始地址 {目标寄存器}

目标寄存器:当寄存器连续时,使用 "-"链接;当寄存器不连续时,使用 ","分隔。

如 "LDM R11,{R6-R9}" 这操作的时R6、R7、R8、R9

如 "LDM R11,{R6,R9}" 这操作的时R6、R9

示例:

将某个地址为起点的地址空间的数据读取到R6~R9寄存器中

这里的R11没有加 "[ ]",但它的数据含义代表地址

列表顺序并不是真正的存储顺序, 存储顺序一定是小编号的寄存器存低地址数据。

如上述LDR指令写成 "LDR R11,{R6,R9,R7,R8}",存储顺序依旧是和 "LDR R11,{R6-R9}"一致。

状态寄存器传送指令

该指令常用于操作系统内部,比如刚上电时,初始化系统时使用该指令。

1、MRS

将CPSR的值读取到指定寄存器

格式:

MRS 目标寄存器 CPSR

示例: 

2、MSR

将指定数据写入到CPSR中

注意:特权模式下可以只写改变CPSR的值,User模式下不能直接改变CPSR的值。

 格式:

MSR CPSR 源操作数

示例:

软中断指令

1、SWI

该指令常用于操作系统内部,比如在User模式下调用系统调用时,Linux就会写一个SWI的指令将User模式转换成SVC模式,从而有足够的权限处理硬件。这个过程其实就是使用SWI指令将用户态切换到内核态。

格式:

SWI #参数

参数:这个值用于区分不同的硬件。用户态切换到内核态都是调用SWI来实现,但究竟调用哪一个硬件CPU并不清楚,此时CPU就需要该参数来得知调用哪一个硬件。

2、模拟实现软中断处理过程

流程描述:

  • 当程序复位,PC指向0x00处,0x00处存放了异常向量表Reset,跳转到MAIN函数。
  • 执行MAIN函数到SWI语句,产生了软中断异常。软中断异常是由SVC模式进行处理,所以CPU自动拷贝CPSR到SPSR_svc中,并且修改CPSR、保存返回地址到LR_svc,最后将PC指向异常向量表对应位置。对于软中断,对应地址为0x80,因此PC跳转到0x80。
  • PC到达0x80后执行跳转指令,跳转到中断处理函数。
  • 中断处理函数也是一个函数,因此需要先入栈保存现场,之后进行相关的操作,最后出栈恢复现场。在出栈时,加上 "^" 符号可以自动将SPSR_svc的数据拷贝到CPSR恢复现场,同时出栈数据可以直接写到PC中,一步实现出栈、恢复现场、跳转。

实验代码如下:

.text
.global _start
_start:

	@异常向量表
	B MAIN				@Reset 			0x00
	B .					@Undef			0x04
	B SWI_Handler		@SWI			0x08
	B .					@Prefetch Abort	0x0C
	B .					@Data Abort		0x10
	B .					@Reserved		0x14
	B .					@IRQ			0x18
	B . 				@FIQ			0x20

	@MAIN中实现R3=R1+R2,并调用一次SWI
MAIN:
	MOV SP,#0x40000020 	@初始化SP,这里是SVC模式下的SP
	MSR CPSR,#0x10	  	@将模式改为User模式,IRQ/FIQ使能
	MOV R1,#1
	MOV R2,#2
	SWI #1				@调用SWI,#1用于区分硬件
	ADD R3,R1,R2
	B STOP

	@SWI_Handler是SWI的中断处理函数,这里也实现R3=R1+R2的功能
SWI_Handler:
	STMFD SP!,{R1,R2,LR}		@入栈保护现场
	MOV R1,#20
	MOV R2,#30
	ADD R3,R1,R2
	LDMFD SP!,{R1,R2,PC}^	@将LR的数据出栈到PC,并把SPSR的值拷贝到CPSR(^的作用)

STOP:
	B STOP

.end

协处理器指令

协处理器指令的分类可以分为:

  • 数据运算指令CDP
  • 存储器访问指令STC、LDC
  • 寄存器传送指令MRC、MCR,用于在协处理器和ARM处理器中的寄存器进行数据交互。

伪指令

1、NOP

NOP是让程序什么都不干,暂停一个机器周期。最终编译之后NOP会被编译成MOV R0,R0

2、LDR

LDR的格式不同,含义也不同。当为 "LDR R1,[R2]" 这种格式时,LDR是一个指令;伪指令的情况有以下情况:

1、赋值任意32位数

当为 "LDR R1,=0x12345678" 这种格式时,LDR是一个伪指令,代表R1=0x12345678。该伪指令可以将任意32位数搬移到寄存器。LDR R1,=0x12345678的转换过程如下:

  • 0x12345678将作为一个不被执行的机器码存放到内存中,这条机器码被存放在全部有效指令之后,在下图中存放在0x58处,因为0x54是最后一条有效指令。
  • LDR的指令将被译码为 "LDR R1,[PC,#偏移值]" 这种格式,在下图中指令被存放在0x30,因为三级流水线的原因,此时PC=0x30+8,因此偏移值 = 0x20。
  • 最终拿到的值是0x58地址当中的数据,就是0x12345678,实现了任意32位数据的赋值。

2、通过标号将地址写入寄存器

当为 "LDR R1,=STOP" 这种格式时,LDR是一个伪指令,代表将STOP标号的地址值存入R1中。

编译后的指令同样是转换成PC加偏移量模式,PC偏移量计算与LDR R1,=0x12345678这种计算方法一样。

3、通过标号将地址当中的值写入寄存器

当为 "LDR R1,STOP" 这种格式时,LDR是一个伪指令,代表将STOP标号的地址当中的值存入R1中。

编译后的指令同样是转换成PC加偏移量模式,PC偏移量计算与LDR R1,=0x12345678这种计算方法一样。

伪操作

伪操作通常以 "." 开头。

1、全局/局部声明

格式:

.global <符号名> 

.local <符号名> 

作用:

将符号声明为全局/局部。

当声明为全局时,所有.s都能访问;当声明为局部时,只有当前.s能访问。

2、宏

格式:

.equ <宏名>,值

作用:

与C语言的#define一样,原值替代。

3、语句封装

格式:

.macro <符号>

@汇编语句

.endm

作用:

类似函数,函数名就是.macro后面跟的符号

4、条件编译

格式:

.if

@汇编语句

.endif

作用:

类似#if #endif,条件满足时才进行编译。

5、重复编译

格式:

.rept <次数>

@汇编语句

.endr

作用:

将区域中的汇编语句重复编译指定次数。

6、弱化符号

格式:

.weak <符号>

作用:

弱化指定的符号,当使用到该符号但未定义该符号时,编译器依旧不报错。

当弱化了符号,并且符号未定义时,编译器会将调用该符号的指令变为NOP,即:一个空操作。

7、申请空间

格式:

.word <初始化值>         @32位,一个字

.byte <初始化值>         @8位,一个字节
.align <以多少字节对齐>  @写3,代表以2^3对齐

.space <申请字节数> <初始化值> @申请指定字节存放数据

.word作用:

在当前指令的地址位置申请空间,并初始化。

.byte作用:

注意:.byte申请空间后,需要再申请冗余空间来进行地址对齐。

.space作用:

注意:.space申请空间后,不一定需要.align来对齐,比如上述是12给字节空间,申请之后地址就是对齐的。

8、标注

格式:

.arm             @告诉编译器这之后是arm指令
@ARM汇编指令

.thumb           @告诉编译器这之后是thumb指令
@Thumb汇编指令    

.text            @告诉编译器这之后是代码段

.end             @告诉编译器汇编代码已结束

http://www.kler.cn/a/399845.html

相关文章:

  • uni-app快速入门(七)--组件路由跳转和API路由跳转及参数传递
  • Linux的桌面
  • Macmini中普通鼠标与TrackPad联动问题解决
  • 4.2 Android NDK 基础概念
  • 帧中继原理与配置
  • HDMI之SBTM
  • 如何手写实现 JSON Parser
  • 自己动手写Qt Creator插件
  • 电脑插入U盘, 电脑显示新增了,但是双击却显示 请将磁盘插入
  • 如何在 gdb 中执行命令
  • css 使用图片作为元素边框
  • shell脚本命令1,保姆级别---清风
  • 【jvm】G1垃圾收集器的特点,为什么低延迟
  • 组成字符串ku的最大次数(字节青训)
  • 农村生活污水排水监测系统:助力乡村生态环境建设
  • 北斗授时板卡 北斗双模PCI总线授时板卡优势分析 双模PCI授时板卡
  • ArchGuard 架构分析器发布:多语言、跨项目架构数据生成,助力 AI 时代知识挖掘...
  • CSS3_伸缩盒模型(十)
  • java.lang.NoSuchMethodError: org.flowable.bpmn.model.FlowNode.isAsynchronous
  • window的wsl(Ubuntu)安装kafka步骤
  • 0基础跟德姆(dom)一起学AI 深度学习05-RNN循环神经网络
  • 智谱AI批量文章生成工具:Python + PyCharm从安装到实战
  • unity修改MeshRender材质球的颜色
  • cpolar内网穿透工具
  • 一、shell脚本基础
  • 流量模型 -20241118