RISCV汇编编程讲解

news2024/9/24 1:21:07

第一章 引言

为什么要讲riscv?

         riscv的特点:

        -诞生于顶尖学术机构:诞生于加州大学伯克利分校的体系结构研究院。吸引了大批的顶尖企业参与(e.g. 谷歌、华为、高通、阿里巴巴为rsicv的发展提供了大量的资金支持和贡献了技术和人才)。

        -精简指令集:指令相对精简,指令格式规整,易于实现和理解。只包含了最基本和常用的指令,避免了复杂和特殊用途的指令,使得处理器的硬件设计更加简洁高效。比如说长度固定(32位/64位)。

        -模块化设计:可以根据不同的应用需求选择不同的模块进行组合。这使得riscv可以适应从嵌入式系统到高性能服务器等不同的应用场景,具有很高的灵活性。

        -开源:例如有多个开源的riscv编译器(如gcc和llvm)可供选择。

        -低功耗:指令集设计精简,处理器的硬件实现相对简单,有助于降低功耗。

命名规范

        RV[###][abc......xyz]。

        [###]表示的是寄存器的位宽,后面的字母标识处理器支持的指令集模块集合。

        例如:RV32MA、RV64GC。

模块化        

        模块化ISA vs 增量式ISA

        RISC-V ISA = 1个基本整数指令集 + 多个可选的扩展指令集。

        基本整数指令集:唯一强制要求实现的基础指令集,其他指令集都是可选的扩展模块。

        扩展模块指令集:RISC-V允许在视线中以可选的形式实现其他标准化和非标准化的指令集扩展。特定组合“IMAFD”被称为“通用”组合,用英文字母G表示。

扩展指令集描述
M整式乘法与除法指令集
A存储器原子指令集
F单精度浮点指令集
D双精度浮点指令集
C压缩指令集(16位长度)
......其它标准化和非标准化指令集

HART

        hart = hardware thread(个人理解为超线程)

        一个hart就是一个虚拟的cpu。一个hart可以不受外界干扰的自主地去获取和执行risc-v指令。

特权级别

levelencodingname 
000user/applicationU
101supervisorS
210reserved 
311machineM

        当运行在用户态时,就是说cpu运行在user级别,进入到内核的时候就是supervisor级别。固件就是machine级别。运行在machine模式是不开虚拟地址的,全部运行在物理地址。

        risc-v芯片一上电首先是进入到machine模式,再进入到supervisor模式,此时也叫保护模式,虚拟地址打开。

        进程的实现依赖于虚拟地址,虚拟地址的实现需要MMU硬件支持。

Control and Status Register(CSR)

        不同的特权级别下时分布对应各自的一套CSR,用于控制和获取响应level下的处理器工作状态。

        高级别的特权可以访问低级别的CSR。比如说machine级别可以访问user级别的csr。

        rsicv定义了专门用于操作csr的指令。

        如果是用户程序,不太需要跟csr打交道。

异常和中断

        异常:主动触发,cpu给你一次改过自新的机会,去执行一段挽救程序;当执行到非法指令的时候,cpu会停掉此指令流跳到一个特殊的地址去执行一段特殊的程序(自己写的程序,想对异常做的处理),执行完之后回到之前的指令再次运行;比如说除0异常;

        中断:被动触发;cpu停掉当前程序,跳转到执行中断处理程序,执行完返回到下一条指令去执行;比如说外设通知你发生一件什么事情,跑去执行别的指令,执行完再回到下条指令,就像中断没有发生过一样。

 ELF介绍

        ELF(Executable Format)是一种unix-like系统上的二进制文件格式标准。

        ELF标准中定义的采用ELF格式的文件分为4类:

ELF文件类型说明实例
可重定位文件(relocatable file)内容包含了代码和数据,可以被链接成可执行文件或共享目标文件linux上的.o文件
可执行文件可以直接执行的文件linux上的a.out文件
共享目标文件内容包含了代码和数据,可以作为链接器的输入,在链接阶段和其他的relacatable file或者shared object file一起链接成新的object file;或者在运行阶段,作为动态链接器的输入,和可执行文件结合,作为进程的一部分来运行。

linux上的.so文件

核心转储文件(core dump file)进程意外终止时,系统可以将该进程的部分内容和终止时的其他状态信息保存到该文件中以供调试分析。linux上的core文件

        ELF文件格式:

ELF Header 
Program Header Table运行视图
.text程序指令
.init做初始化的一些指令
.data数据:全局变量等
.bss 
...... 

Section Header Table

 

从链接角度去描述了这个文件的内容

        .text和.init等这些信息放置的时候都会section(节)对齐,但是在运行的时候会合在一起以节省空间,于是有了segment(段)概念,segment的信息存放于program header table中。segment fault就是因为出错的时候在内存中失败。

0a26db0bd61245a29c645fb07b36ce17.png

        ELF文件处理相关工具:

       Binutils官网地址:https://www.gnu.org/software/binutils/

        ar:归档文件,将多个文件打包成一个大文件。

        as:被gcc调用,输入汇编文件,输出目标文件工链接器ld连接。

        ld:gnu链接器,被gcc调用,它把目标文件和各种库文件结合在一起,重定位数据,并链接符号引用。

        objcopy:执行文件格式转换。

        objdump:显示ELF文件的信息。

        readelf:显示更多ELF格式文件的信息(包括DWARF调试信息)

        示例1:

gcc -c hello.c
readelf -h hello.o  // 查看头信息

readelf -S hello.o  // 查看section信息
readelf -SW hello.o

        实例2:

gcc -g -c hello.c
objdump -S hello.o // 显示汇编指令

 嵌入式开发

        嵌入式开发是一种比较综合性的技术,它不单指纯粹的软件开发技术,也不单是一种硬件配置技术;它是在特定的硬件环境下针对某款硬件进行开发,是一种系统级别的与硬件结合比较紧密的软件开发技术。程序并不是运行在本地,而是运行在特殊的硬件上。

        参与编译和运行的机器根据其角色可以分成以下三类:

        -build系统:生成编译器可执行程序的计算机。编译器在build系统上编译出来的。 

        -host系统:运行编译器可执行程序,编译链接应用程序的计算机系统。

        -target系统:运行应用程序的计算机系统。

        根据build/host/target的不同组合我们可以得到如下的编译方式分类:

        -本地(native)编译:build==host==target

        -交叉(cross)编译:build==host!=target

QEMU

        QEMU是一套由(Fabrice Bellard)编写的以GPL许可证分发源码的计算机系统模拟软件,在GNU/Linux平台上使用广泛。 

        QEMU,支持多种体系架构。譬如:IA-32(x86),AMD 64, MIPS 32/64,RISC-V 32/64等等。

        QEMU有两种主要运作模式:

        -user mode: 直接运行应用程序 (比如说hello.o运行直接输出“hello”)

        -system mode:模拟整个计算机系统,包括中央处理器及其他周边设备。

第二章 汇编语言编程

 基本组成

        汇编文件一般后缀为.S或.s,.S包含了预处理的语句,.s就是纯粹的汇编语句。

        一个完整的RISC-V汇编程序有多条语句(statement)组成。一条典型的RISC-V汇编语言由3部分组成:

[label:] [operation] [comment]

        打方括号表示可选。

        -label表示一个标号,必须以":"结尾。label相当于一个地址,给这个地址起了个名字。是这条指令存放在内存的地址。

        -operation可以由以下多种类型:

                -instruction(指令):直接对应二进制机器指令的字符串

                -preudo-instruction(伪指令):为了提高编写代码的效率,可以用一条伪指令指示汇编器产生多条实际的指令(instruction)。(要在汇编器的手册里查看定义)

                -directive(指示/伪操作):通过类似指令的形式(以“.”开头),通知汇编器如何控制代码的产生等,不对应具体的指令。属于汇编器自己定义的一些语法。(要在汇编器的手册里查看定义)

                -macro:采用.macro/.endm自定义的宏

        例子:


.macro do_nothing      #directive
        nop            #preudo-instruction
        nop            #preudo-instruction
.endm

    .text              #directive  告诉汇编器生成的指令要放到text的section中
    .global _start     #directive  _start是个全局变量,外部可见,有点像extern


_start:                 #label
    li x6, 5            #preudo-instruction
    li x7, 4            #preudo-instruction
    add x5, x6, x7      #instruction
    do_nothing          #calling macro


    .end                #End of file

RISC-V汇编指令操作对象

        寄存器:

        -在RISC-V中,Hart在执行算术逻辑运算时所操作的数据必须直接来自寄存器

        -32个通用寄存器,x0~x31;

 寄存器别名
x0读0寄存器,其值永远为0,写操作对其无效。
x1ra,用于存储函数调用后的返回地址。
x2sp,栈指针。
x3gp,全局指针。一半指向全局变量和静态变量。它可以帮助快速访问全局数据区域,提高对全局变量的访问效率。
x4tp,线程指针。主要用于多线程编程,指向当前线程的特定数据结构或信息。
x5-x7,x28-x31t0-t6,临时寄存器。通常用于临时存储或中间结果的保存。在程序执行过程中,可用于暂存数据、参与运算等。
x8-x9,x18-x27s0-s11,保存寄存器。用于在函数调用过程中保存寄存器的值,以便在函数返回时恢复这些值。
x10-x17a0-a7,参数寄存器。用于传递函数的参数。参数可以通过这些寄存器传递给被调用函数,方便函数内部对参数进行访问和处理。

 RISC-V汇编指令类型

31-2524-2019-1514-1211-76-0 
funct7rs2rs1funct3rdopcodeR-type
imm[]imm[]rs1funct3rdopcodeI-type
imm[]rs2rs1funct3imm[4:0]opcodeS-type
imm[]rs2rs1funct3imm[4:1[11]]opcodeB-type
imm[]imm[]imm[]imm[]rdopcodeU-type
imm[]imm[]imm[]imm[]rdopcodeJ-type

        R-type:(register),每条指令中有三个fields,用于指定3个寄存器参数。

         I-type:(Immediate),每条指令除了带有两个寄存器参数外,还带有一个立即数参数(宽度为12bits)。

        S-type: (Store),每条指令除了带有两个寄存器参数外,还带有一个立即数参数(宽度为12bits,但fields的组织方式不同于I-type)。(用来访问内存的指令)

        B-type: (Branch),每条指令除了带有两个寄存器参数外,还带有一个立即数参数(宽度为12bits,但取值为2的倍数)。(跟分支跳转有关)

        U-type:(Upper),每条指令含有一个寄存器参数再加上一个立即数参数(宽度为20bits,用于表示一个立即数的高20位)。(auipc、lui)

        J-type:(Jump),每条指令含有一个寄存器参数再加上一个立即数参数(宽度为20bits)。

小端序的概念

         riscv是小端序编指令。

算数指令

指令语法描述例子
ADDADD RD, RS1, RS2RS1和RS2的值相加,结果保存到RDadd x5, x6, x7
SUBSUB RD, RS1, RS2RS1的值减去RS2的值,结果保存到RDsub x5, x6, x7
ADDIADDI RD, RS1, RS2RS1的值和IMM相加,结果保存到RDaddi x5, x6, x7
LUILUI RD, IMM构造一个32位的数,高的20位存放IMM,低12位清零,结果保存到RDlui x5, 0x12345
AUIPCAUIPC RD, IMM构造一个32位的数,高20位存放IMM,低12清零。结果和PC相加保存到RD。auipc x5, 0x12345

基于算术运算指令实现的其他伪指令

伪指令语法等价指令指令描述例子
LILI RD, IMMLUI和ADDI的组合将立即数加载到RD中li x5, 0x12345678
LALA RD, LABELAUIPC和ADDI组合为RD加载一个地址值la x5,label
NEGNEG RD, RSSUB RD, X0, RS对RS中的值取反并将结果存放在RD中neg x5, x6
MVMV RD, RSADDI RD, RS, 0将RS中的值拷贝到RD中mv x5, x6
NOPNOPADDI x0, x0, 0什么也不做nop

LI(Load Immediate)

        用算数指令构建一个大数  

#例子:构建一个大数

# imm is in range of [-2048, +2047]
# 0x80000
lui x5, 0x80
addi x5, x0, 0x80


# imm is NOT in the range of [-2048, +2047]
# and the most-significant-bit of "lower-12" is 0
# 0x12345001
lui x6, 0x12345
addi x6, x6, 0x001


# imm is NOT in the range of [-2048, +2047]
# and the most-significant-bit of "lower-12" is 1
# 0x12345FFF
lui x7 0x12346
addi x7, x7, -1

        用伪指令

li x7 xxxxxxxxx

LA(Load Address)

语法LA RD, LABEL 
例子la x5, foo 

        LA是一个伪指令

        具体编程时给出需要加载的label,编译器会根据实际情况利用auipc和其他指令自动生成正确的指令序列。

        常用语加载一个函数或者变量的地址。

        例子

_start:
    la x5, _start    # x5 = _start
    jr x5

        反汇编出来很可能就是一条auipc指令。

逻辑运算指令

指令格式语法描述例子
ANDR-typeAND RD, RS1, RS2RD = RS1 & RS2and x5, x6, x7
ORR-typeOR RD, RS1, RS2RD = RS1 | RS2or x5, x6, x7
XORR-typeXOR RD, RS1, RS2RD = RS1 ^ RS2xor x5, x6, x7
ANDII-typeANDI RD, RS1, IMMRD = RS1 & IMMandi x5, x6, 20
ORII-typeORI RD, RS1, IMMRD = RS1 | IMMor x5, x6, 20
XORII-typeXORI RD, RS1, IMMRD = RS1 ^ IMMxor x5, x6, 20

        所有的逻辑指令都是按位操作。

逻辑移位运算指令

指令格式语法描述例子
SLLR-typeSLL RD, RS1, RS2逻辑左移sll x5, x6, x7
SRLR-typeSRL RD, RS1, RS2逻辑右移srl x5, x6, x7
SLLII-typeSLLI RD, RS1, IMM逻辑左移立即数slli x5, x6, 3
SRLII-typeSRLI RD, RS1, IMM逻辑右移立即数srli x5, x6, 3

        无论是逻辑左移还是右移,补足的都是0。

算术移位运算指令​​​​​​​​​​​​​​

指令格式语法描述例子
SRAR-typeSRA RD RS1, RS2算术右移sra x5, x6, x7
SRAII-typeSRAI RD, RS1, IMM算术右移立即数srai x5, x6, 3

        算术右移时按照符号位值补足。

内存读写指令

5e8a39d3a31e42b1bfc6d93497f033f7.png

 

内存读

指令格式语法描述例子
LBI-type        LB RD, IMM(RS1)读取一个8bits的数据到RD中,内存地址=RS1 + IMM,数据保存到RD之前执行sign-extended。lb x5, 40(x6)
LBUI-type        LBU RD, IMM(RS1)数据在保存到RD之间会执行zero-extendedlbu x5, 40(x6)
LHI-type        LH RD, IMM(RS1)读取一个16bit的数据。lh x5, 40(x6)
LHUI-type         LHU RD, IMM(RS1)读取伊恩16bit的数据。lhu x5, 40(x6)
LWI-type         LW RD, IMM(RS1)读取一个32bit的数据。lw x5, 40(x6)

内存写

指令格式语法描述例子
SBS-type       SB SR2, IMM(RS1)将RS2寄存器中低8bits的数据写出到内存中,内存地址=RS1 + IMM。sb x5, 40(x6)
SHS-type        SH SR2, IMM(RS1) sh x5, 40(x6)
SWS-type         SW SR2, IMM(RS1) sw x5, 40(x6)

条件分支指令

指令格式语法描述例子
BEQB-typeBEQ RS1, RS2, IMM如果相等则跳转beq x5, x6, 100
BNEB-typeBNE RS1, RS2, IMM如果不相等则跳转bne x5, x6, 100
BLTB-typeBLT RS1, RS2, IMM按照有符号数方式比较,如果小于则跳转blt x5, x6, 100
BLTUB-typeBLTU RS1, RS2, IMM按照无符号数方式比较,如果小于则跳转bltu x5, x6, 100
BGEB-typeBGE RS1, RS2, IMM按照有符号方式比较,大于等于就跳转bge x5, x6, 100
BGEUB-typeBGEU RS1, RS2, IMM按照无符号方式比较,大小等于就跳转

bgeu x5, x6, 100

伪指令语法等价指令描述
BLEBLE RS, RT, OFFSETBGE RT, RS, OFFSET有符号方式比较,<=
BLEUBLEU RS, RT, OFFSETBGEU RT, RS, OFFSET 无符号方式比较,<=
BGTBGT RS, RT, OFFSETBLT RT, RS, OFFSET有符号比较,>
BGTUBGTU RS, RT, OFFSETBLTU RT, RS, OFFSET无符号比较,>
BEQZBEQZ RS, RT, OFFSETBNE RS, x0, OFFSET==0
BNEZBNEZ RS, RT, OFFSETBLT RS, x0, OFFSET!=0
BLTZBLTZ RS, RT, OFFSETBGE x0, RS, OFFSET< 0
BLEZBLEZ RS, RT, OFFSETBLT RT, R0, OFFSET<= 0
BGTZBGTZ RS, RT, OFFSETBGE RT, RS, OFFSET> 0
BGEZBGEZ RS, OFFSETBGE RT, RS, OFFSET>= 0

无条件跳转

JAL(Jump And Link)

语法JAL RD, LABEL
例子jal x1, label

        jal指令用于调用子过程,跳转到指定位置,并且把下一条指令的地址写入RD,保存为返回地址。

JALR(Jump And Link Register)

语法JALR RD, IMM(RS1)
例子jalr x0, 0(x5)

        jalr指令同样用于调用子过程,跳转到指定位置,并且把下一条指令的地址写入RD,保存为返回地址。

例子

int a = 1;
int b = 1;

void sum()
{
    a = a + b;
    return;       // jalr x0, 0(x5)   不需要保存下一条指令的地址
}

void _start()
{
    sum();        // jal x5, sum

    other codes
    ...
}

思考:

        如何解决更远距离的跳转?

AUIPC X6, IMM-20
JALR X1, X6, IMM-12

指令寻址模式总结

        所谓寻址指的是指令中定位操作数或者地址的方式

寻址模式解释例子
立即数寻址操作数是指令本身的一部分。addi x5,x6,20
寄存器寻址操作数存放在寄存器中,指令中指定访问的寄存器从而获取该操作数。add x5, x6, x7
基址寻址操作数在内存中,指令中通过指定寄存器(基址base)和立即数(偏移量offset),通过base+offset的方式获得操作数在内存中的地址从而获取该操作数。sw x5,40(x6)
PC相对寻址在指令中通过PC和指令中的立即数相加获得目的地址的值。beq x5,x6,100

第三章 函数的调用

函数调用过程概述

85f5cc99cd4c433b9839d1b177edb4bd.png

        当caller和callee不是一个人写的,这个时候就需要制定一套规定。​​​​​​​

f6b99cc24ca34a12aeab2cdc204d3bb4.png

函数调用的规则

寄存器名ABI名(编程用名)用途约定谁负责在函数调用过程中维护这些寄存器
x0zero读取时总为0,写入时不起任何效果N/A

x1

ra存放函数返回地址Caller
x2sp存放栈指针callee
x5-x7,x28-x31

t0-t2,t3-t6

临时寄存器,callee可能会使用这些寄存器,所以callee不保证这些寄存中的值在函数调用过程中保持不变。对于caller来说,如果需要的话应在调用前保存这些值。caller
x8,x9,x18-x27s0,s1,s2-s11保存寄存器。callee需要保证这些寄存器的值在函数返回后仍然维持函数之前的原值。callee
x10,x11a0,a1参数寄存器,用于在函数调用过程中保存第一个和第二参数。caller
x12-x17a2,a7参数寄存器。如果有更多的参数则要利用到栈。caller

ac621579c0ba45af9890755fcba6a742.png

        函数执行过程的三部曲:

        -保存现场

        -执行函数内容

        -恢复现场                

例子(尾调用)

2aae26c431724cf8a111f021a8a5cf19.png

例子(非尾调用)

915b7ecef85341888f0b9972aa37b17b.png

riscv编程与c混合编程

汇编调用c语言

5a5d6d208b394f21a1a5cd745e03b636.png

c语言调用汇编

int foo(int a, int b)
{
    int c;
    asm volatile(
        "add %[sum], %[add1], %[add2]"  // 汇编指令
        :[sum] = r'(c)                  // 输出操作数列表(可选)
        :[add1]"r"(a), [add2]"r"(b)     // 输入操作数列表(可选)
    );
    return c;
}

第四章 UART串行接口调用

状态寄存器读写

语法CSRRW RD, CSR, RS1 
例子csrrw t6, mscratch, t6

t6 = mscratch;mscratch = t6

语法CSRRS RD, CSR, RS1 
例子csrrs x5, mie, x6x5 = mie; mie |= x6

720546e5117f46da89523af5a385ab1c.png

UART(统一异步接收传输协议)

        通过异步收发传输器,是一种双全工、异步串行通信方式,它在发送数据时将并行数据转换成串行数据来传输,在接收数据时将接收到的串行数据转换成并行数据,可以实现双全工传输和接收。

        UART串口通信需要两根信号线来实现,一根用于串口发送,另外一根负责串口接收。

        UART在发送或接收过程中的一帧数据由4部分组成,起始位(低电平)、数据位、奇偶校验位和停止位(高电平)。

        起始位标志着一帧数据的开始,停止位标志着一帧数据的结束,数据位是一帧数据中的有效数据。

        

4f59758c99884d3a826d5e8a76c928d0.png

        信号与管脚定义

345f5663b7a5478b806564eb1b523ea8.png

        qemu 模拟了 ns16550a 芯片,通过查阅资料可知内存映射将对应的基地址映射到了 0x1000_0000 处。

bf8b914e6cf1433790b4c2db23d36cf4.png

        platform.h编写

/* This machine puts UART register here in physical memory. */
#define UARTO 0x10000000

        uart.c编写

#define UART_REG(reg) ((volatile uint8_t*)(UART0 + reg))
#define uart_read_reg(reg) (*(UART0 + reg))
#define uart_write_reg(reg, v) (*(UART0 + reg)) = (v)

         NS16550a编程接口介绍

917164c231044503a68844dddbcb3ec4.png

        (进入程序)

 

 

 

 

代码链接

https://github.com/plctlab/riscv-operating-system-mooc

https://gitee.com/unicornx/riscv-operating-system-mooc

 

 

 

 

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

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

相关文章

【计算机网络】名词解释--网络专有名词详解(更新)

在网络通信中&#xff0c;有许多专业术语和概念&#xff0c;它们共同构成了网络通信的基础。以下是一些常见的网络术语及其定义和相互之间的关系&#xff1a; 一、网络基础 1.1 电路交换&#xff1a;电路交换是一种在数据传输前建立专用通信路径的通信方式。在通信开始前&…

RAG 技术原理

目录 RAG 技术原理背景和概念实现步骤1. ChatGPT/GLM 等大语言模型的调用2. 读取知识库数据3. 文本索引与答案检索4. 文本嵌入与向量检索5. 文本多路召回与重排序6. 文本问答Promopt优化 原创链接 RAG 技术原理 背景和概念 在自然语言处理领域&#xff0c;大型语言模型&#x…

使用 C 语言实现字符走迷宫 DFS算法应用

使用 C 语言实现字符走迷宫 DFS算法应用 迷宫问题是一个经典的编程问题&#xff0c;通常用于算法训练。我们将通过使用 C 语言来实现一个字符迷宫的求解&#xff0c;其中玩家可以控制字符在迷宫中移动&#xff0c;直到找到出口。 1. 问题描述 我们将设计一个二维迷宫&#xf…

Unity--AnimationCurve动画曲线设置

参考文章&#xff1a;https://blog.csdn.net/qq_20179331/article/details/131309128 打开Clip文件点击Curves选项&#xff0c;选中想要编辑的动作关键帧&#xff0c;右键选择Auto 这样动画就变成线性的了

爆改YOLOv8 |利用 iAFF迭代注意力改进C2f,高效涨点

1&#xff0c;本文介绍 iAFF的核心思想是通过细致的注意力机制优化特征融合&#xff0c;从而提升卷积神经网络的性能。它不仅解决了因尺度和语义不一致导致的特征融合问题&#xff0c;还引入了多尺度通道注意力模块&#xff0c;提供了一个统一且通用的特征融合方案。此外&…

二分查找算法:朴素二分+左右边界二分力扣实战应用

目录&#xff1a; 1、二分查找算法简介 2、算法原理及时间复杂度分析 2.1 朴素二分算法 3.2 查找左右边界的二分算法 3.2.1 查找左边界 3.2.2 查找右边界 3.3 时间复杂度分析 3、二分查找算法模版 3.1 朴素二分模版 3.2 查找左右边界的二分模版 4、算法应用【leetco…

企业收款码,自动统计职员绩效-微信支付商家版

一、企业收款码 在快节奏的商业世界中&#xff0c;效率与精准是企业成功的关键。微信支付商家版企业收款码&#xff0c;为你开启全新的绩效统计时代。 告别繁琐的传统统计方式&#xff0c;无需再耗费大量时间人工整理数据。企业收款码自动统计职员绩效&#xff0c;每一笔交易都…

Cortex-A7的GIC(通用中断控制器):中断处理状态机

0 资料 ARM Generic Interrupt Controller Architecture version 2.0 Architecture Specification1 中断处理状态机 1.1 中断处理状态说明及状态机转换图 说明&#xff1a; Inactive&#xff1a;未激活&#xff0c;中断无效。中断非挂起或非激活。 Pending&#xff1a;挂起&a…

iZotope Ozone 11 Advanced:专业音频制作与母带处理的巅峰之作

iZotope Ozone 11 Advanced是一款专为音频工程师、制作人和音乐人设计的顶级音频后期制作软件&#xff0c;无论是Mac还是Windows平台&#xff0c;都能为用户提供无与伦比的音频处理体验。该软件集成了最先进的人工智能技术和一系列精密的音频处理工具&#xff0c;让音频作品的最…

还在烦恼Cosplay论坛开发?探索PHP+Vue的完美解决方案!

&#x1f393; 作者&#xff1a;计算机毕设小月哥 | 软件开发专家 &#x1f5a5;️ 简介&#xff1a;8年计算机软件程序开发经验。精通Java、Python、微信小程序、安卓、大数据、PHP、.NET|C#、Golang等技术栈。 &#x1f6e0;️ 专业服务 &#x1f6e0;️ 需求定制化开发源码提…

STM32定时器PWM输出

STM32定时器PWM&#xff08;脉冲宽度调制&#xff09;输出原理&#xff0c;在使用固件库时&#xff0c;主要涉及定时器的配置以及PWM信号的生成。以下是对该原理的详细解释&#xff1a; 一、PWM基本概念 PWM&#xff08;Pulse Width Modulation&#xff09;是一种通过改变脉冲…

docker 容器内文件传到宿主机上

sudo docker cp 容器名&#xff1a;文件路径 宿主机路径 ylshy-Super-Server:~$ pwd /home/yl ylshy-Super-Server:~$ ^C ylshy-Super-Server:~$ sudo docker cp ylafl:/opt/live555/testProgs/rtsp.pcap /home/yl Successfully copied 4.61kB to /home/yl ylshy-Super-Server…

自适应学习率(Datawhale X 李宏毅苹果书 AI夏令营)

传统的梯度下降方法在优化过程中常常面临学习率设置不当的问题。固定的学习率在训练初期可能过大&#xff0c;导致模型训练不稳定&#xff0c;而在后期可能过小&#xff0c;导致训练速度缓慢。为了克服这些问题&#xff0c;自适应学习率方法应运而生。这些方法通过动态调整学习…

Django使用视图动态输出CSV以及PDF的操作详解例子解析

代码示例&#xff1a; 在Django中&#xff0c;使用视图动态输出CSV和PDF文件是一个常见的需求&#xff0c;可以通过Python标准库中的csv模块和reportLab库来实现。以下是一些详细的操作步骤和示例代码。 CSV文件的动态输出 首先&#xff0c;需要导入Python的csv模块&#xf…

JSP的九大内置对象及其作用详解

JSP的九大内置对象及其作用详解 1. request对象2. response对象3. pageContext对象4. session对象5. application对象6. out对象7. config对象8. page对象9. exception对象 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 在JSP&#xff08…

<数据集>骨折检测数据集<目标检测>

数据集格式&#xff1a;VOCYOLO格式 图片数量&#xff1a;2060张 标注数量(xml文件个数)&#xff1a;2060 标注数量(txt文件个数)&#xff1a;2060 标注类别数&#xff1a;7 标注类别名称&#xff1a;[elbow positive, shoulder fracture, fingers positive, wrist positi…

0818-0824面试题目和复习整理

根据面试问的问题整理一下 1. 并查集 int n 1005; // n根据题目中节点数量而定&#xff0c;一般比节点数量大一点就好 vector<int> father vector<int> (n, 0); // C里的一种数组结构// 并查集初始化 void init() {for (int i 0; i < n; i) {father[i] i;…

Kubernetes部署相关概念

本文封面由 凯楠&#x1f4f8;友情提供 Kubernetes部署相关概念概览 容器运行时&#xff08;container runtime&#xff09;&#xff1a; 是负责在计算机操作系统上创建、运行和管理容器的软件组件。它是整个容器化环境中的关键组成部分&#xff0c;与操作系统内核紧密交互&a…

linux(Ubuntu )搭C++ 最新版GDAL完整教程

在前面的文章中主要是介绍如何在windows系统下利用python安装gdal库&#xff0c;如下&#xff1a; 如何快速安装GDAL 在linux环境下python安装gdal也可以利用现成的whl文件&#xff0c;但是安装c GDAL环境的比较麻烦&#xff0c;目前网络上大多是安装的老版本的教程&#xff…

springboot3 SecurityConfig SecurityFilterChain 需要使用CorsFilter,实际是CorsWebFilter

使用springboot3做微服务开发&#xff0c;由于网关gateway使用webFlux&#xff0c;因此导致实际类型是CorsWebFilter&#xff0c;但是在public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception方法中&#xff0c;项目启动报错…