ECLIC中断流程及实际应用 —— RISC-V中断机制(二)

在长期的嵌入式开发实践中,对中断机制的理解始终停留在表面层次,特别当开发者长期局限于纯软件抽象层面时,对中断机制的理解极易陷入"知其然而不知其所以然"的困境,这种认知的局限更为明显;随着工作需要不断深入底层技术,对硬件机制的了解逐渐加深,并积累了大量的学习笔记。借此机会,我将这些零散的知识进行系统化梳理,既是对自身知识的复盘,也希望能为相关领域的开发者提供些许帮助和参考。关于RISC-V中断机制的分析,本文将从硬件实现原理和软件应用以下两个方面来展开介绍:

RISC-V CLINT、PLIC及芯来ECLIC中断机制分析 —— RISC-V中断机制(一)
ECLIC中断流程及实际应用 —— RISC-V中断机制(二)

背景

中断(Interrupt)机制,即处理器内核在顺序执行程序指令流的过程中突然被别的请求打断而中止执行当前的程序,转而去处理别的事情,待其处理完了别的事情,然后重新回到之前程序中断的点继续执行之前的程序指令流。
中断相关的基本知识要点:

  • 打断处理器执行的“别的请求”便称之为中断请求(Interrupt Request),“别的请求”的来源便称之为中断源(Interrupt Source),中断源通常来自于内核外部(称之为外部中断源),也可以来自于内核内部(成为内部中断源)。
  • 处理器转而去处理的“别的事情”便称之为中断服务程序(Interrupt Service Routine,ISR)
  • 中断处理是一种正常的机制,而非一种错误情形。处理器收到中断请求之后,需要保存当前程序的现场,简称为“保存现场”。等到处理完中断服务程序后,处理器需要恢复之前的现场,从而继续执行之前被打断的程序,简称为“恢复现场”。
  • 可能存在多个中断源同时向处理器发起请求的情形,需要对这些中断源进行仲裁,从而选择哪个中断源被优先处理。此种情况称为“中断仲裁”,同时可以给不同的中断分配级别和优先级以便于仲裁,因此中断存在着“中断级别”和“中断优先级”的概念。

芯来N级别处理器内核实现了一个“改进型内核中断控制器(Enhanced Core Local Interrupt Controller,ECLIC)”,可用于多个中断源的管理。N级别处理器内核中的所有类型(除了调试中断之外)的中断都由ECLIC统一进行管理。

1 寄存器

有详细介绍说过ECLIC相关的寄存器,下面介绍中断处理流程使用的CSR寄存器:用来保存控制信息。

1.1 硬件自动填写的寄存器

  • mepc(Machine Exception Program Counter)
    保存发生异常或中断时的PC值。
    如果中断处理需要恢复到异常指令后一条指令进行执行,就需要正确判断将 pc 寄存器加上多少字节。

  • mcause(Machine Cause Register)
    记录中断是否是硬件中断,以及具体的中断原因,如:

    • Interrupt Bit(最高位):1 表示中断,0 表示异常。
    • Exception Code(低 31 位):具体原因编码(如 0x0B 表示外部中断)。

异常编码表:
在这里插入图片描述

  • mtval(Machine Trap Value Register)
    存储与异常相关的附加信息(如非法地址、非法指令编码)。

    • 非法地址异常:mtval 记录访问的非法地址。
    • 非法指令异常:mtval 存储非法指令的二进制编码。
    • 对于中断(非异常),mtval 通常无意义,可能保留为 0

1.2 指示硬件处理中断的寄存器

  • mtvec(Machine Trap Vector Base Address Register)
    设置机器模式(M-mode)下中断和异常的入口地址基址。存储了一个基址 BASE 和模式 MODE:
    • MODE 为 0 表示 直接模式,即遇到中断便跳转至 同一入口地址(mtvec.BASE)。
    • MODE 为 1 表示 向量模式,中断跳转到 mtvec.BASE + 4 * cause,异常仍使用统一入口。

以上是官方中断入口,芯来的eclic是基于clic,这里是有改动的,正常情况下mtvec是作为异常处理入口的,中断入口是由mtvt2定义。芯来mtvec定义如下:

在这里插入图片描述

  • mstatus(Machine Status Register)
    控制全局中断使能及特权模式切换;如:

    • MIE(Machine Interrupt Enable):全局中断开关。若为 0,所有中断被屏蔽。
    • MPP(Machine Previous Privilege):记录中断前的特权模式(如 M/S/U-mode),用于 mret 返回。
      在这里插入图片描述
  • mie(Machine Interrupt Enable Register)
    用来控制具体类型中断的使能,如:

    • MEIE(Machine External Interrupt Enable):外部中断(如 PLIC/ECLIC 中断)使能。
    • MTIE(Machine Timer Interrupt Enable):定时器中断使能。
    • MSIE(Machine Software Interrupt Enable):软件中断使能。
      在这里插入图片描述
  • mip(Machine Interrupt Pending Register)
    和 mie 相对应,标记中断的挂起状态(Pending Bits)

    • MEIP(Machine External Interrupt Pending):外部中断挂起位。
    • MTIP(Machine Timer Interrupt Pending):定时器中断挂起位。
    • MSIP(Machine Software Interrupt Pending):软件中断挂起位。
  • mtvt
    寄存器保存中断向量表的基址(在CLIC模式下),基址至少对齐64字节边界

  • mtvt2
    用于指示所有ECLIC中断共享的公共基处理程序的入口地址
    在这里插入图片描述

  • mscratch
    寄存器的用处会在实现线程时起到作用,在中断处理开始时,将当前线程的上下文指针保存到 mscratch,再从 mscratch 加载中断栈指针;感兴趣可以自行学习下。

  • msubm
    芯来自定义的CSR msubm寄存器保存当前机器子模式和当前陷阱之前的机器子模式

在这里插入图片描述

  • jalmnxti
    芯来自定义的CSR jalmnxti,用来减少中断延迟并加速中断尾部链接。
    jalmnxti包括mnxti(Next Interrupt Handler Address and Interrupt-Enable CSR)的所有功能,此外还包括启用中断、处理下一个中断、跳转到下一个中断入口地址以及跳转到中断处理程序。(感兴趣可以自行继续深入学习下)

实际通过修改jalmnxti和ra的地址的值,在中断嵌套和咬尾时,可以节省保存上下文(CSR和通用寄存器)的开销,

1.4 临时寄存器

  • pushmepc
  • pushmsubm
  • pushmcause

芯来自定义了通过CSR指令csrrwi将msubm、mepc、mcause的值存储在以SP为基址的内存空间中,该指令将CSR寄存器的值存储在SP+1*4地址中。

2 ECLIC中断处理流程

2.1 整体流程(主要以非向量中断为例)

当一个hart发生中断时,整个中断流程需要软硬协作完成,下图是eclic中断处理,包括:进入以及推出全部流程流程

在这里插入图片描述

  1. 硬件接收到中断信号,硬件自动更新CSR寄存器

    • 更新内核退出中断时的返回地址,存储在mepc(1、该地址就是中断打断的PC值(在中断结束之后,回到被停止执行的程序点), 2、mepc软件可以显示修改)
      • ①、mcause.EXCCODE存放中断ID,以便软件查询;②、如果当前中断抢占了低优先级中断,mcause.MPIL将更新为minstatus.MIL的值,处理中断后,将使用mcause.MPIL的值来恢复mintcause.MIL的值;③、如果是向量模式中断,mcause.minhv的值将更新为1,在完成从中断向量表中取出存储的目标地址,然后再跳转到目标地址中去后,mcause.minhv域的值清除为0
      • ①、mstatus.MPIE更新为mstatus.MIE的值,mstatus.MIE置0(屏蔽所有中断);②、处理器的当前特权模式(Privilege Mode)切换到机器模式(Machine Mode)mstatus.MPP从特权模式将切换到机器模式;
      • ①、msubm.PTYP域的值被更新为中断发生前的Machine Sub-Mode(msubm.TYP域的值)②、msubm.TYP域的值则被更新为“中断处理模式”(反映当前的模式已经是“中断处理模式”)
  2. 跳转到共享中断入口地址,保存中断上下文(非向量中断)(寄存器clicintattr[i]的shv域决定中断是向量中断还是非向量中断)

    • 跳入到mtvt2.CMMON-CODE-ENTRY(mtvt2.MTVT2EN = 1);
    • 将一些通用寄存器(ra/tp/t0-t6/a0-a7)(保存中断上下文)保存到堆栈中;
    • 将CSR mepc、mcause、msubm保存到堆栈中,确保后续的抢占中断可以被正确处理;
      • 如果被配置成为向量处理模式,则该中断被处理器内核响应后,处理器直接跳入该中断的向量入口(Vector Table Entry)存储的目标地址(ISR地址)
      • 如果被配置成为非向量处理模式,则该中断被处理器内核响应后,处理器直接跳入所有中断共享的入口地址
  3. 执行Nuclei自定义指令“csrrw ra,CSR_JALMNXTI,ra”,如果没有待处理的中断,则该指令将被视为空操作;否则进入下面步骤

    • 直接跳入该中断的向量入口(Vector Table Entry)存储的目标地址,即该中断源的中断服务程序**(Interrupt Service Routine,ISR)**中去
    • 硬件置位全局中断使能位mstatus.MIE,此时可以接受新中断,以形成中断嵌套。
    • 把当前PC(csrrw ra,CSR_JALMNXTI,ra)写入返回地址ra寄存器,达到JAL(Jump and Link)的效果,即:在执行完中断handle之后,将再次执行该指令“csrrw ra,CSR_JALMNXTI,ra”,进而重新判断是否有还未处理的中断(pending),进而形成中断咬尾
  4. 从中断服务函数中返回后,软件来恢复中断上下文

    • mstatus.MIE置0,屏蔽所有中断,确保操作的原子性
    • 从堆栈中恢复中断前CSR寄存器(msubm、mepc、mcause)以及通用寄存器(ra/tp/t0-t6/a0-a7/sp)的值
  5. 软件执行mret退出中断处理程序,硬件将自动更新CSR寄存器

    • ①、mcause.MPIL的值恢复minstatus.MIL的值,minstatus.MIL的值会被恢复到中断前的原始值;②、使用mcause.MPIE的值恢复minstatus.MIE的值,minstatus.MIE的值会被恢复到触发中断前的值;③、mcause.MPP特权模式从中断模式退出,恢复为中断前的模式,(同样也会更新mstatus.MPIE和mcause.MPP的值,见最后NOTE)。
    • 硬件将处理器Machine Sub-Mode的值恢复为msubm.PTYP域的值
    • 跳转到mepc定义的PC,继续执行之前被中止的程序流。

NOTE:
mstatus.MPIE域和mstatus.MPP域的值与mcause.MPIE域和mcause.MPP域的值是镜像关系,即,在正常情况下,mstatus.MPIE域的值与mcause.MPIE域的值总是完全一样,mstatus.MPP域的值与mcause.MPP域的值总是完全一样。

2.2 向量中断与非向量中断

ECLIC的每个中断源均可以设置成向量或者非向量处理(通过寄存器clicintattr[i]的shv域),向量处理模式和非向量处理模式二者有较大的差别

2.1章节主要是以非向量中断为例介绍的中断处理流程,这里把前面的一些和向量中断不同的点再总结下,方便和向量中断做对比

2.2.1 非向量中断处理模式

在这里插入图片描述
1、非向量处理模式,则该中断被处理器内核响应后,处理器会直接跳入到所有非向量中断共享的入口地址,该入口地址可以通过软件进行设置

  • 如果配置CSR寄存器mtvt2的最低位为0(上电复位默认值),则所有非向量中断共享的入口地址由CSR寄存器mtvec的值(忽略最低2位的值)指定。由于mtvec寄存器的值也指定异常的入口地址,因此,意味着在这种情况下,异常和所有非向量中断共享入口地址

  • 如果配置CSR寄存器mtvt2的最低位为1芯来SDK的bootloader里配置为1了,将异常和非向量中断入口分开,不用判断是中断还是异常了,提升效率),则所有非向量中断共享的入口地址由CSR寄存器mtvt2的值(忽略最低2位的值)指定。

2、如2.1章节步骤二描述,进入所有非向量中断共享的入口地址之后,处理器会开始执行一段共有的软件代码

  • 首先保存CSR寄存器mepc、mcause、msubm入堆栈。保存这几个CSR寄存器是为了保证后续的中断嵌套能够功能正确,因为新的中断响应会重新覆盖mepc、mcause、msubm的值,因此需要将它们先保存入堆栈
  • 保存若干通用寄存器(处理器的上下文)入堆栈
  • 然后执行一条特殊的指令“csrrw ra, CSR_JALMNXTI, ra”。如果没有中断在等待(Pending),则该指令相当于是个Nop指令不做任何操作;如果有中断在等待(Pending),执行该指令后处理器会
    • 直接跳入该中断的向量入口(Vector Table Entry)存储的目标地址,即该中断源的中断服务程序(Interrupt Service Routine,ISR)中去。
    • 在跳入中断服务程序的同时,硬件也会同时打开中断的全局使能,即,设置mstatus寄存器的MIE域为1。打开中断全局使能后,新的中断便可以被响应,从而达到中断嵌套的效果。
    • 在跳入中断服务程序的同时,“csrrw ra, CSR_JALMNXTI, ra”指令还会达到JAL(Jump and Link)的效果,硬件同时更新Link寄存器的值为该指令的PC自身作为函数调用的返回地址。因此,从中断服务程序函数返回后会回到该“csrrw ra, CSR_JALMNXTI, ra”指令重新执行,重新判断是否还有中断在等待(Pending),从而达到中断咬尾的效果。
    • 在中断服务程序的结尾处同样需要添加对应的恢复上下文出栈操作。并且在CSR寄存器mepc、mcause、msubm出堆栈之前,需要将中断全局使能再次关闭,以保证mepc、mcause、msubm恢复操作的原子性(不被新的中断所打断)。

2.2.2 向量中断的处理

在这里插入图片描述
1、如果被配置成为向量处理模式,则该中断被处理器内核响应后,处理器会**直接跳入该中断的向量入口(Vector Table Entry)**存储的目标地址,即该中断源的中断服务程序(Interrupt Service Routine,ISR)

2、向量处理模式具有如下特点:

  • 向量处理模式时处理器会直接跳到中断服务程序,并没有进行上下文的保存,因此,中断响应延迟非常之短,从中断源拉高到处理器开始执行中断服务程序中的第一条指令,基本上只需要硬件进行查表和跳转的时间开销,理想情况下约6个时钟周期。

  • 对于向量处理模式的中断服务程序函数,一定要使用特殊的__attribute__((interrupt))来修饰中断服务程序函数。

  • 向量处理模式时,由于在跳入中断服务程序之前,处理器并没有进行上下文的保存,因此,理论上中断服务程序函数本身不能够进行子函数的调用。

  • 如果不小心调用了其他子函数,只要使用了__attribute__((interrupt))来修饰中断复位函数,编译器就会自动插入一段代码进行上下文的保存。(实际使用中不推荐调用子函数)

  • 处理器在响应向量中断后,mstatus寄存器中的MIE域将会被硬件自动更新成为0(中断被全局关闭,从而无法响应新的中断)。因此向量处理模式默认是不支持中断嵌套的,为了达到向量处理模式且又能够中断嵌套的效果,可以在中断服务例程里依次加入以下操作来实现中断嵌套效果。

    • 保存CSR寄存器mepc、mcause、msubm入堆栈。
    • 重新打开中断的全局使能(mstatus.MIE置1)
    • 执行中断程序内容
    • 关闭中断全局使能,恢复上下文出栈操作

3、对于向量处理模式的中断而言,由于在跳入中断服务程序之前,处理器并没有进行上下文的保存,因此进行“中断咬尾”的意义不大,因此,向量处理模式的中断,没有“中断咬尾”处理能力。(除非在向量中断复位函数里进行中断上下文以及返回地址的处理,但这样不如一开始就注册为非向量中断)

2.2.3 二者区别

这里就简单总结对比下:

对比项向量中断(Vectored Interrupt)非向量中断(Non-Vectored Interrupt)
入口地址每个中断有独立的入口地址(由向量表定义)。所有中断共享统一入口地址。
开销低(硬件自动跳转IRQ,无需软件处理中断上下文)。高(需要从统一入口进入,需要软件处理中断上下文)。
中断嵌套支持(需要在中断服务例程中手动处理上下文来支持)支持
中断咬尾不支持支持
IRQ定义void __INTERRUPT isr_uart() { ... }void isr_uart() { ... }
应用场景需要快速响应的高优先级外设低优先级或非实时外设

NOTE:
非向量模式的中断处理函数,中断函数前面一定不要加__INTERRUPT 这个关键字描述,否则编译器会用mret 指令,导致中断提前返回了中断前的代码,而不是返回到common_entry 这里
而向量中断需要使用__attribute__((interrupt))来修饰,让编译器来处理该服务例程

2.3 中断抢占和中断咬尾

实际上通过前面的前面的介绍,对于中断抢占和中断咬尾,基本上已经能了解个七七八八了,这里再简单总结下,并举例说明下。

2.3.1 基本概念

1、中断嵌套
处理器内核正在处理某个中断的过程中,可能有一个级别更高的新中断请求到来,处理器可以中止当前的中断服务程序,转而开始响应新的中断,并执行其“中断服务程序”,如此便形成了中断嵌套(即前一个中断还没响应完,又开始响应新的中断),并且嵌套的层次可以有很多层。

2、中断咬尾
处理器内核正在处理某个中断的过程中,可能有新中断请求到来,但是**“新中断的级别”低于或者等于“当前正在处理的中断级别”**,因此,新中断不能够打断当前正在处理的中断(因此不会形成嵌套)

2.3.2 中断嵌套处理流程

1、非向量中断
假设中断源30、31、32这三个中断源先后到来,且“中断源32的级别” > “中断源31的级别”> “中断源30的级别”,那么后来的中断便会打断之前正在处理的中断形成中断嵌套
在这里插入图片描述
2、向量中断
假设中断源30、31、32这三个中断源先后到来,且“中断源32的级别” > “中断源31的级别”> “中断源30的级别”,那么后来的中断便会打断之前正在处理的中断形成中断嵌套。
在这里插入图片描述
向量中断不同点是,1、中断入口不一致(中断的服务函数地址),2、中断服务函数里软件实现中断上下文处理

2.3.3 中断咬尾处理流程

假设中断源30、29、28这三个中断源先后到来,且“中断源30的级别” >= “中断源29的级别”>= “中断源28的级别”,那么后来的中断不会打断之前正在处理的中断(不会形成中断嵌套),但是会被置于等待(Pending)状态。当中断源30完成处理后,将会直接开始中断源29的中断处理,省掉中间的“恢复上下文”和“保存上下文”过程。

在这里插入图片描述

2.3.4 总结

非向量中断总是能够支持中断嵌套和中断咬尾的;
向量中断则是可以通过在中断服务程序中通过软件处理来支持中断嵌套,但不支持中断咬尾。
另外,并未给出向量中断和非向量中断相互嵌套的例子,不过大家可以自行分析下。

3 实际应用

这里结合芯来开源SDK中demo_eclic代码及qemu(RISC-V汇编学习(四)—— RISCV QEMU平台搭建(基于芯来平台))来演示下,向量中断与非向量中断以及中断的嵌套。

中断注册入口初始化共享中断入口流程代码,自行参考芯来开源sdk中的驱动及BootLoader学习(参考下面源码链接)。

3.1、源码分析

芯来提供了一个软件中断和timer中断的示例,源码点击链接即可查看:
Gitee: https://gitee.com/Nuclei-Software/nuclei-sdk/blob/master/application/baremetal/demo_eclic/demo_eclic.c
Github: https://github.com/Nuclei-Software/nuclei-sdk/blob/master/application/baremetal/demo_eclic/demo_eclic.c.

源码执行结果,展示部分打印

-------------------
[IN TIMER INTERRUPT]timer interrupt hit 0 times
[IN TIMER INTERRUPT]trigger software interrupt
[IN TIMER INTERRUPT]software interrupt will run when timer interrupt finished
[IN TIMER INTERRUPT]timer interrupt end
[IN SOFTWARE INTERRUPT]software interrupt hit 0 times
[IN SOFTWARE INTERRUPT]software interrupt end
-------------------
[IN TIMER INTERRUPT]timer interrupt hit 1 times
[IN TIMER INTERRUPT]trigger software interrupt
[IN TIMER INTERRUPT]software interrupt will run when timer interrupt finished
[IN TIMER INTERRUPT]timer interrupt end
[IN SOFTWARE INTERRUPT]software interrupt hit 1 times
[IN SOFTWARE INTERRUPT]software interrupt end

分析

  • 程序中分别注册了:
    • 1、timer中断:高优先级、非向量中断
    • 2、软件中断:低优先级、向量中断
timer_intlevel = HIGHER_INTLEVEL;
swirq_intlevel = LOWER_INTLEVEL;// initialize timer
setup_timer();
// initialize software interrupt as vector interrupt
returnCode = ECLIC_Register_IRQ(SysTimerSW_IRQn, ECLIC_VECTOR_INTERRUPT,ECLIC_LEVEL_TRIGGER, swirq_intlevel, 0, eclic_msip_handler);
// inital timer interrupt as non-vector interrupt
returnCode = ECLIC_Register_IRQ(SysTimer_IRQn, ECLIC_NON_VECTOR_INTERRUPT,ECLIC_LEVEL_TRIGGER, timer_intlevel, 0, eclic_mtip_handler);
  • 实现:
    通过设置core内部的MTIMERCMP寄存器,来触发timer中断,之后在timer中断服务例程里配置MSIP来触发软件中断

根据打印可以发现程序这里实现了中断咬尾效果,即:
1、低优先级中断无法打断高优先级中断
2、每次都会先触发非向量中断(timer中断),然后再触发向量中断(soft中断)
3、在高优先级的非向量中断退出后再次执行"csrrw ra, CSR_JALMNXTI, ra",此时还有低优先级的中断在pending中,就会继续执行低优先级的软件中断,从而形成中断咬尾

另外,这里把软件中断配置成非向量中断(记得去掉IRQ的__INTERRUPT修饰)效果也是一样的,
但是把timer中断换成向量中断(不支持中断咬尾)是不行的。(第一个触发的中断是非向量中断可以实现中断咬尾)

3.2 中断嵌套实现

我们在芯来提供的demo_eclic基础上来做些修改:

只需要打开#define SWIRQ_INTLEVEL_HIGHER 1,就会从新定义优先级: soft中断>timer中断

执行代码,部分打印如下:

-------------------
[IN TIMER INTERRUPT]timer interrupt hit 0 times
[IN TIMER INTERRUPT]trigger software interrupt
[IN TIMER INTERRUPT]software interrupt will run during timer interrupt
[IN SOFTWARE INTERRUPT]software interrupt hit 0 times
[IN SOFTWARE INTERRUPT]software interrupt end
[IN TIMER INTERRUPT]timer interrupt end
-------------------
[IN TIMER INTERRUPT]timer interrupt hit 1 times
[IN TIMER INTERRUPT]trigger software interrupt
[IN TIMER INTERRUPT]software interrupt will run during timer interrupt
[IN SOFTWARE INTERRUPT]software interrupt hit 1 times
[IN SOFTWARE INTERRUPT]software interrupt end
[IN TIMER INTERRUPT]timer interrupt end

可以看到在低优先级timer中断执行时,将被高优先级soft中断打断,从而形成中断嵌套。

这里把软件中断配置成非向量中断(记得去掉IRQ的__INTERRUPT修饰)效果也是一样的,
后者把timer中断换成向量中断也是可行的(1、IRQ加上的__INTERRUPT修饰 2、IRQ里开始加上SAVE_IRQ_CSR_CONTEXT();,结束加上RESTORE_IRQ_CSR_CONTEXT();)。

以上就是芯来eclic中断相关的内容,如有纰漏,还请各位看官大佬予以指出

参考:
芯来科技N级别指令集架构

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

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

相关文章

计算机网络-LDP标签发布与管理

前面学习了LDP建立邻居,建立会话,今天来学习在MPLS中的标签发布与管理。 在MPLS网络中,下游LSR决定标签和FEC的绑定关系,并将这种绑定关系发布给上游LSR。LDP通过发送标签请求和标签映射消息,在LDP对等体之间通告FEC和…

Go语言运算符详解

文章目录 1. 算术运算符2. 关系运算符3. 逻辑运算符4. 位运算符5. 赋值运算符6. 其他运算符运算符优先级注意事项 Go语言提供了与其他语言类似的运算符,包括算术运算符、关系运算符、逻辑运算符、位运算符、赋值运算符等。这些运算符即可满足基本的运算需求。 1. 算…

Selenium模拟人类行为,操作网页的方法(全)

看到有朋友评论问,用selenium怎么模仿人类行为,去操作网页的页面呢? 我想了想,这确实是一个很大的点,不应该是一段代码能解决的, 就像是,如果让程序模拟人类的行为。例如模拟人类买菜,做饭&am…

RabbitMQ的工作队列模式和路由模式有什么区别?

RabbitMQ 的工作队列模式(Work Queues)和路由模式(Routing)是两种不同的消息传递模式,主要区别在于消息的分发逻辑和使用场景。以下是它们的核心差异: 1. 工作队列模式(Work Queues&#xff09…

牛客练习赛138(首篇万字题解???)

赛时成绩如下: 1. 小s的签到题 小s拿到了一个比赛榜单,他要用最快的速度找到签到题,但是小s脑子还是有点晕,请你帮帮小s,助力他找到签到题。 比赛榜单是一个 2 行 n 列的表格: 第一行是 n 个大写字母&#…

linux0.11内核源码修仙传第十六章——获取硬盘信息及根目录挂载

🚀 前言 书接第十四章:linux0.11内核源码修仙传第十四章——进程调度之fork函数,在这一节博客中已经通过fork进程创建了一个新的进程1,并且可以被调度,接下来接着主线继续走下去。希望各位给个三连,拜托啦&…

mobile自动化测试-appium webdriverio

WebdriverIO是一款支持mobile app和mobile web自动化测试框架,与appium集成,完成对mobile应用测试。支持ios 和android两种平台,且功能丰富,是mobile app自动化测试首选框架。且官方还提供了mobile 应用测试example代码&#xff0…

Kubernetes排错(十):常见网络故障排查

通用排查思路 Kubernetes 集群内不同服务之间的网络通信出现异常,表现为请求超时、连接失败或响应缓慢,导致服务间依赖关系中断,依赖服务的功能不可用或性能下降,甚至可能波及整个微服务架构,引发连锁反应&#xff0c…

PyTorch 张量与自动微分操作

笔记 1 张量索引操作 import torch ​ # 下标从左到右从0开始(0->第一个值), 从右到左从-1开始 # data[行下标, 列下标] # data[0轴下标, 1轴下标, 2轴下标] ​ def dm01():# 创建张量torch.manual_seed(0)data torch.randint(low0, high10, size(4, 5))print(data->,…

接口的基础定义与属性约束

在 TypeScript 中,接口(Interface)是一个非常强大且常用的特性。接口定义了对象的结构,包括对象的属性和方法,可以为对象提供类型检查和约束。通过接口,我们可以清晰地描述一个对象应该具备哪些属性和方法。…

高效全能PDF工具,支持OCR识别

软件介绍 PDF XChange Editor是一款功能强大的PDF编辑工具,支持多种操作功能,不仅可编辑PDF内容与图片,还具备OCR识别表单信息的能力,满足多种场景下的需求。 软件特点 这款PDF编辑器完全免费,用户下载后直接…

OpenCV 中用于背景分割的一个类cv::bgsegm::BackgroundSubtractorGMG

操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 cv::bgsegm::BackgroundSubtractorGMG 是 OpenCV 中用于背景分割的一个类,它实现了基于贝叶斯推理的背景建模算法(Bayesi…

MongoDB知识框架

简介:MongoDB 是一个基于分布式文件存储的数据库,属于 NoSQL 数据库产品,以下是其知识框架总结: 一、数据模型 文档:MongoDB 中的数据以 BSON(二进制形式的 JSON)格式存储在集合中,…

WEBSTORM前端 —— 第2章:CSS —— 第8节:网页制作2(小兔鲜儿)

目录 1.项目目录 2.SEO 三大标签 3.Favicon 图标 4.版心 5.快捷导航(shortcut) 6.头部(header) 7.底部(footer) 8.banner 9.banner – 圆点 10.新鲜好物(goods) 11.热门品牌(brand) 12.生鲜(fresh) 13.最新专题(topic) 1.项目目录 【xtx-pc】 ima…

1、RocketMQ 核心架构拆解

1. 为什么要使用消息队列? 消息队列(MQ)是分布式系统中不可或缺的中间件,主要解决系统间的解耦、异步和削峰填谷问题。 解耦:生产者和消费者通过消息队列通信,彼此无需直接依赖,极大提升系统灵…

[Linux网络_71] NAT技术 | 正反代理 | 网络协议总结 | 五种IO模型

目录 1.NAT技术 NAPT 2.NAT和代理服务器 3.网线通信各层协议总结 补充说明 4.五种 IO 模型 1.什么是IO?什么是高效的IO? 2.有那些IO的方式?这么多的方式,有那些是高效的? 异步 IO 🎣 关键缺陷类比…

Unity基础学习(八)时间相关内容Time

众所周知,每一个游戏都会有自己的时间。这个时间可以是内部,从游戏开始的时间,也可以是外部真实的物理时间,时间相关内容 主要用于游戏中 参与位移计时 时间暂停等。那么我们今天就来看看Unity中和时间相关的内容。 Unity时间功能…

Java游戏服务器开发流水账(1)游戏服务器的架构浅析

新项目立项停滞,头大。近期读老项目代码看到Java,笔记记录一下。 为什么要做服务器的架构 游戏服务器架构设计具有多方面的重要意义,它直接关系到游戏的性能、可扩展性、稳定性以及用户体验等关键因素 确保游戏的流畅运行 优化数据处理&a…

计算机视觉与深度学习 | 基于Transformer的低照度图像增强技术

基于Transformer的低照度图像增强技术通过结合Transformer的全局建模能力和传统图像增强理论(如Retinex),在保留颜色信息、抑制噪声和平衡亮度方面展现出显著优势。以下是其核心原理、关键公式及典型代码实现: 一、原理分析 1. 全局依赖建模与局部特征融合 Transformer的核…

Linux 文件目录管理常用命令

pwd 显示当前绝对路径 cd 切换目录 指令备注cd -回退cd …返回上一层cd ~切换到用户主目录 ls 列出目录的内容 指令备注ls -a显示当前目录中的所有文件和目录,包括隐藏文件ls -l以长格式显示当前目录中的文件和目录ls -hl以人类可读的方式显示当前目录中的文…