TinyEMU源码分析之中断处理

TinyEMU源码分析之中断处理

  • 1 触发中断
  • 2 查询中断
    • 2.1 查询中断使能与pending状态(mie和mip)
    • 2.2 查询中断总开关与委托(mstatus和mideleg)
      • 2.2.1 M模式
      • 2.2.2 S模式
      • 2.2.3 U模式
  • 3 处理中断
    • 3.1 获取中断编号
    • 3.2 检查委托
    • 3.3 进入中断
      • 3.3.1 配置mtvec
      • 3.3.2 配置stvec
    • 3.4 执行中断服务程序
    • 3.5 退出中断
      • 3.5.1 处理mret指令
      • 3.5.2 处理sret指令
  • 4 总结

本文属于《 TinyEMU模拟器基础系列教程》之一,欢迎查看其它文章。
本文中使用的代码,均为伪代码,删除了部分源码。

本文,以TinyEMU中M模式下的时钟中断为例,进行说明。

1 触发中断

mtimer是实现在M模式下的定时器,它位于CLINT控制器内部。
并给该计时器,定义了两个64 位宽的寄存器mtime和mtimecmp。

  • mtime,用于反映当前计时器的计数值
  • mtimecmp,用于设置计时器的比较值

当mtime 中的计数值 >= mtimecmp 中设置的比较值时,计时器便会产生时钟中断

时钟中断,会一直拉高,直到软件重新写mtimecmp 寄存器的值,使得mtimecmp值大于mtime值,从而将计时器中断清除。

在TinyEMU源码,riscv_machine.c中riscv_machine_get_sleep_duration函数,如下:

static int riscv_machine_get_sleep_duration(VirtMachine *s1, int delay)
{delay1 = m->timecmp - rtc_get_time(m);if (delay1 <= 0) {riscv_cpu_set_mip(s, MIP_MTIP);delay = 0;} else {/* convert delay to ms */delay1 = delay1 / (RTC_FREQ / 1000);if (delay1 < delay)delay = delay1;}...
}

当mtimecmp >= 当前时间时,调用riscv_cpu_set_mip函数,将0x80写入mip寄存器(即mip.MTIP=1),表示M模式下时钟中断处于等待响应状态。

2 查询中断

在riscv_cpu_template.h中,取指、译码、执行主循环处理glue函数,如下:

static void no_inline glue(riscv_cpu_interp_x, XLEN)(RISCVCPUState *s,int n_cycles1)
{for(;;) {// 获取PCs->pc = GET_PC(); // check pending interruptsraise_interrupt(s);// 取指、译码、执行...}
}

调用riscv_cpu.c中raise_interrupt函数,来处理中断,如下:

static __exception int raise_interrupt(RISCVCPUState *s)
{mask = get_pending_irq_mask(s); // 检测是否有中断或异常if (mask == 0)return 0;irq_num = ctz32(mask); // mask转为中断号或异常号raise_exception(s, irq_num | CAUSE_INTERRUPT); // 处理中断或异常return -1;
}

在处理中断前,我们需要调用get_pending_irq_mask函数,来检查是否有中断需要处理,返回非0,表示有中断待处理。
接下来,介绍get_pending_irq_mask函数的具体实现。

2.1 查询中断使能与pending状态(mie和mip)

get_pending_irq_mask函数,如下所示:

static inline uint32_t get_pending_irq_mask(RISCVCPUState *s)
{uint32_t pending_ints, enabled_ints;// part1:查询mip和mie寄存器pending_ints = s->mip & s->mie;if (pending_ints == 0)return 0; // 未发生中断...
}

mie寄存器,可使能和关闭中断(1为使能,0为关闭),如下所示:
在这里插入图片描述

  • SSIE:表示S模式下,软件中断使能位
  • MSIE:表示M模式下,软件中断使能位
  • STIE:表示S模式下,时钟中断使能位
  • MTIE:表示M模式下,时钟中断使能位
  • SEIE:表示S模式下,外部中断使能位
  • MEIE:表示M模式下,外部中断使能位

mip寄存器,可指示中断已发生(1为发生,0为未发生),如下所示:
在这里插入图片描述

  • SSIP:表示S模式下的,软件中断处于等待响应状态
  • MSIP:表示M模式下的,软件中断处于等待响应状态
  • STIP:表示S模式下的,时钟中断处于等待响应状态
  • MTIP:表示M模式下的,时钟中断处于等待响应状态
  • SEIP:表示S模式下的,外部中断处于等待响应状态
  • MEIP:表示M模式下的,外部中断处于等待响应状态

当M模式下时钟中断发生时,则:

  • mie.MTIE,必然为1;
  • mip.MTIP,必然也为1。

因此,只有当mie&mip不为0时,才表示发生了中断,需要进行中断处理。
这里代码中,pending_ints = 0x80,表明发生了M模式下时钟中断,该中断需要被处理。

2.2 查询中断总开关与委托(mstatus和mideleg)

查询委托,也是在get_pending_irq_mask函数,如下所示:

static inline uint32_t get_pending_irq_mask(RISCVCPUState *s)
{	// part2:查询mstatus和mideleg寄存器enabled_ints = 0;switch(s->priv) {case PRV_M:if (s->mstatus & MSTATUS_MIE)enabled_ints = ~s->mideleg;break;case PRV_S:enabled_ints = ~s->mideleg; // s->mideleg = 0x222,enabled_ints = 0xfffffdddif (s->mstatus & MSTATUS_SIE) // s->mstatus.sie = 1enabled_ints |= s->mideleg; // enabled_ints = 0xffffffffbreak;default:case PRV_U:enabled_ints = -1;break;}return pending_ints & enabled_ints;
}

接下来,分别介绍,各模式下的判断逻辑。

2.2.1 M模式

    case PRV_M:if (s->mstatus & MSTATUS_MIE)enabled_ints = ~s->mideleg;break;

mstatus寄存器的mie位域,表示M模式下,全局中断开关;只有打开时,才会处理中断,否则抛弃。
若当前运行,在M模式下时:

  • 若mideleg.mie关闭,则enabled_ints为0,表明在M模式下,接收到任何中断,都被抛弃。
  • 若mideleg.mie打开,表明允许处理M模式下中断,但是需排除mideleg中指定委托到S模式处理的中断,用取反操作,来屏蔽掉这些中断的bit位,并置位未委托的中断bit位。得到的enabled_ints,该值中bit位为1,对应的这些中断,就是需要在M模式下处理的。

最后,返回值为(pending_ints & enabled_ints),该值为非0时,表示在M模式下可处理的中断。

换言之,在M模式下,可处理的中断,必须满足:

  • mie中对应bit为1:表示打开xx模式yy中断开关
  • mip中对应bit为1:表示xx模式yy中断等待处理
  • mstatus.mie为1:表示打开M模式中断总开关
  • mideleg中对应bit为0:表示xx模式yy中断未委托给S模式处理

注意:
mie、mip、mideleg这三个寄存器的字段结构定义,是完全一样的,理解了这一点,有助于理解本函数,这些逻辑与或操作的含义。
在这里插入图片描述

2.2.2 S模式

    case PRV_S:enabled_ints = ~s->mideleg; // s->mideleg = 0x222,enabled_ints = 0xfffffdddif (s->mstatus & MSTATUS_SIE) // s->mstatus.sie = 1enabled_ints |= s->mideleg; // enabled_ints = 0xffffffffbreak;

mstatus寄存器的sie位域,表示S模式下,全局中断开关;只有打开时,才会处理中断,否则抛弃。
若当前运行,在S模式下时:

  • 若mideleg.sie为0,表示关闭S模式中断,因此委托到S模式的这些中断,统统不能处理,需要忽略。~s->mideleg表示只处理未委托的中断(默认在M模式处理),后续可从S陷入M,去处理这些中断。
  • 若mideleg.sie为1,表示打开S模式中断,因此委托到S模式的这些中断,可以处理;并且未委托的中断(默认在M模式处理),可通过后续从S陷入M,去处理的。这两类中断,都可以处理,因此使用enabled_ints |= s->mideleg

最后,返回值为(pending_ints & enabled_ints),该值为非0时,表示在S模式下可处理的中断。

换言之,在S模式下,可处理的中断,必须满足:

  • mie中对应bit为1:表示打开xx模式yy中断开关
  • mip中对应bit为1:表示xx模式yy中断等待处理
  • mstatus.sie
    (1) sie为0时,只能处理未委托的中断(mideleg对应bit为0),后续通过S陷入M处理。
    (2) sie为1时,可处理未委托的中断(mideleg对应bit为0),后续通过S陷入M处理;以及委托的中断(mideleg对应bit为1),就在S下直接处理。

运行在S模式下时,对于非委托中断,其默认处理方式,就是陷入M模式;因此在S模式下,对这些非委托中断,均做了放过处理,未拦截。

这里,处理M模式时钟中断时,当前运行在S模式下,所以应该走这条分支,以继续处理。

2.2.3 U模式

    case PRV_U:enabled_ints = -1; // enabled_ints = 0xffffffffbreak;

若当前运行,在U模式下时:

  • enabled_ints = 0xffffffff,处理接受所有中断。

最后,返回值为(pending_ints & enabled_ints),该值为非0时,表示在U模式下可处理的中断。

换言之,在U模式下,可处理的中断,必须满足:

  • mie中对应bit为1:表示打开xx模式yy中断开关
  • mip中对应bit为1:表示xx模式yy中断等待处理

在U模式下,仅检查上述2项条件,因为U模式本身不具备处理中断的能力,因此对于满足条件的这些中断,需要全部做放过处理。在后续,可通过检查mideleg进行委托到S处理,或者非委托陷入M模式处理。

3 处理中断

static __exception int raise_interrupt(RISCVCPUState *s)
{mask = get_pending_irq_mask(s); // 检测是否有中断或异常if (mask == 0)return 0;irq_num = ctz32(mask); // mask转为中断号或异常号raise_exception(s, irq_num | CAUSE_INTERRUPT); // 处理中断或异常return -1;
}

在调用get_pending_irq_mask函数,查询到mask为非0,下面进行中断的处理。

3.1 获取中断编号

然后,会调用ctz32函数,查询mask中,第几位为1。

static inline int ctz32(uint32_t a)
{int i;if (a == 0)return 32;for(i = 0; i < 32; i++) {if ((a >> i) & 1)return i;}return 32;
}

例如:
发生M模式时钟中断时,mask=0x80,那么irq_num=7,表示中断编号(Exception Code)为7。
那么,irq_num | CAUSE_INTERRUPT,结果为0x80000007。

3.2 检查委托

然后,会调用raise_exception函数,如下:

static void raise_exception(RISCVCPUState *s, uint32_t cause)
{raise_exception2(s, cause, 0);
}
static void raise_exception2(RISCVCPUState *s, uint32_t cause,target_ulong tval)
{BOOL deleg;target_ulong causel;// part1 : check delegif (s->priv <= PRV_S) {/* delegate the exception to the supervisor priviledge */if (cause & CAUSE_INTERRUPT)deleg = (s->mideleg >> (cause & (MAX_XLEN - 1))) & 1;elsedeleg = (s->medeleg >> cause) & 1;} else {deleg = 0;}...
}

在raise_exception2函数中,首先判断当前模式,如果<=S,即U和S模式,那么才进行委托判断,也就是说:

  • 只有在U和S模式下,发生中断时,才能委托到S模式处理;
  • 在M模式下,发生中断时,不能委托,只能在M模式处理。

这里当前为S模式,因此会进入分支。
然后,再判断cause的最高位:

  • 为1,表示中断。
  • 为0,表示异常。

其实无论是中断,还是异常,都是从cause中取出Exception Code,并判断mideleg中第Exception Code位的值deleg:
如果deleg为0,表示不委托,会在M模式下处理此中断;
如果deleg为1,表示委托,此中断会被委托到S模式处理。

这里M模式时钟中断,对应deleg为0,即mideleg.MTIP=0。
因此,此中断需要在M模式下处理

3.3 进入中断

检查委托,得到deleg值。
然后会将cause扩展为64位,以便写入寄存器中,如下:

static void raise_exception2(RISCVCPUState *s, uint32_t cause,target_ulong tval)
{...// part2 : enter interrupt// 将cause扩展为64位// 即0x80000007 => 0x8000000000000007causel = cause & 0x7fffffff;if (cause & CAUSE_INTERRUPT)causel |= (target_ulong)1 << (s->cur_xlen - 1);// 委托if (deleg) {s->scause = causel;s->sepc = s->pc;s->stval = tval;s->mstatus = (s->mstatus & ~MSTATUS_SPIE) |(((s->mstatus >> s->priv) & 1) << MSTATUS_SPIE_SHIFT);s->mstatus = (s->mstatus & ~MSTATUS_SPP) |(s->priv << MSTATUS_SPP_SHIFT);s->mstatus &= ~MSTATUS_SIE;set_priv(s, PRV_S);s->pc = s->stvec;} // 不委托else {s->mcause = causel;s->mepc = s->pc;s->mtval = tval;s->mstatus = (s->mstatus & ~MSTATUS_MPIE) |(((s->mstatus >> s->priv) & 1) << MSTATUS_MPIE_SHIFT);s->mstatus = (s->mstatus & ~MSTATUS_MPP) |(s->priv << MSTATUS_MPP_SHIFT);s->mstatus &= ~MSTATUS_MIE;set_priv(s, PRV_M);s->pc = s->mtvec;}
}

当deleg为0时,表示不委托,在M模式处理中断。
进入中断服务程序之前,需要完成以下操作:

  • 更新mcause
  • 更新mepc
  • 更新mtval
  • 更新mstatus
  • 切换到M模式
  • pc = mtvec,跳转到M模式异常处理入口地址

当deleg为1时,表示委托,在S模式处理中断。
进入中断服务程序之前,需要完成以下操作:

  • 更新scause
  • 更新sepc
  • 更新stval
  • 更新mstatus
  • 切换到S模式
  • pc = stvec,跳转到S模式异常处理入口地址

更新这些寄存器,主要是做现场保存,比如进入中断处理前的PC,模式等,以便在退出中断处理后,可以恢复到中断前的状态(具体参考RISCV规范文档)。

这里有一个问题,mtvec或stvec,到底什么时候配置的,以及指向何处?
接下来,我们来解释这个问题。

3.3.1 配置mtvec

在Bootloader初始化过程中,会执行riscv-pk\machine\mentry.S中,如下代码:

  # write mtvec and make sure it sticksla t0, trap_vector			// t0 = &trap_vectorcsrw mtvec, t0				// mtvec = t0

也就是,把trap_vector地址,写入mtvec寄存器(配置M模式,异常处理入口地址)。
mentry.S中trap_vector地址处,代码如下:
在这里插入图片描述
当为了处理中断或异常,而进入M模式时,PC会跳转到M模式异常向量表trap_vector,开始执行第一条指令csrrw sp, mscratch, sp,直到处理完毕后(当然中间可能会有一些跳转),执行最后一条指令mret,返回之前的模式。硬件在响应mret指令时,会自动将PC跳转到发生异常前的位置。

第一条与最后一条指令之间,这段代码,我们可以理解为:M模式下的异常服务程序
在Bootloader初始化时,只有先配置了mtvec,后续M模式下的异常,才能正常响应。

3.3.2 配置stvec

在进入OS阶段,Linux初始化过程中,会执行arch/riscv/kernel/head.S中,如下代码:

relocate:/* Relocate return address */li a1, PAGE_OFFSET		// a1 = PAGE_OFFSETla a0, _start			// a0 = _startsub a1, a1, a0			// a1 = a1 - a0add ra, ra, a1			// ra = ra + a1/* Point stvec to virtual address of intruction after satp write */la a0, 1f				// a0 = 1fadd a0, a0, a1			// a0 = a0 + a1csrw stvec, a0			// stvec = a0 (stvec = 1f + PAGE_OFFSET - _start)

也就是,把S模式异常处理入口地址(1f + PAGE_OFFSET - _start),写入stvec寄存器,(可参考《一篇分析RISC-V Linux汇编启动过程》,或者《内核代码分析(linux系统riscv架构)》)。

该入口地址,其实位于arch/riscv/kernel/entry.S中trap_entry地址处,代码如下:
在这里插入图片描述
直到处理完毕后(当然中间可能会有一些跳转),执行最后一条指令sret,返回之前的模式。硬件在响应sret指令时,会自动将PC跳转到发生异常前的位置。

第一条与最后一条指令之间,这段代码,我们可以理解为:S模式下的异常服务程序
在Linux初始化时,只有先配置了stvec,后续S模式下的异常,才能正常响应。

3.4 执行中断服务程序

回到TinyEMU源码上来,看看如何M模式时钟中断。
在raise_exception2函数中,进入M模式,并跳转到mtvec指向的M模式异常处理入口地址,会执行riscv-pk\machine\mentry.S中,以下关键代码:

  # Yes.  Simply clear MTIE and raise STIP.li a0, MIP_MTIP					// a0 = MIP_MTIPcsrc mie, a0						// mie &= ~a0\li a0, MIP_STIP					// a0 = MIP_STIPcsrs mip, a0						// mip |= a0...mret
  • mie.MTIP=0,关闭M模式时钟中断
  • mip.STIP=1,S模式时钟中断处于等待响应状态(中断注入)

然后,便通过mret退出,结束处理。
可以看出:

  • 中断服务程序,并没有特别处理此时钟中断,仅仅是切到M模式下,向S模式注入了一个时钟中断。
  • 类似于,实现了将M模式时钟中断,“委托”到S模式处理的效果。注入的STIP中断,与正常中断处理流程完全一致(下一轮,重新再走一遍“查询中断”=>“处理中断”,这些各个步骤)。

3.5 退出中断

由于退出中断时,固件/OS,往往会调用mret或sret指令,来恢复中断前的状态和模式。
我们看看TinyEMU,是如何响应mret和sret指令的。

3.5.1 处理mret指令

当TinyEMU执行mret指令时,会调用riscv_cpu.c中handle_mret函数,如下所示:

static void handle_mret(RISCVCPUState *s)
{int mpp, mpie;mpp = (s->mstatus >> MSTATUS_MPP_SHIFT) & 3;/* set the IE state to previous IE state */mpie = (s->mstatus >> MSTATUS_MPIE_SHIFT) & 1;s->mstatus = (s->mstatus & ~(1 << mpp)) |(mpie << mpp);/* set MPIE to 1 */s->mstatus |= MSTATUS_MPIE;/* set MPP to U */s->mstatus &= ~MSTATUS_MPP;set_priv(s, mpp);s->pc = s->mepc;
}

退出中断服务程序后,需要完成以下操作:

  • 恢复mstatus
  • M模式,切换到中断前的模式
  • pc = mepc,跳转中断前的程序PC地址

这些操作,都是做现场恢复(具体参考RISCV规范文档)。

3.5.2 处理sret指令

当TinyEMU执行sret指令时,会调用riscv_cpu.c中handle_sret函数,如下所示:

static void handle_sret(RISCVCPUState *s)
{int spp, spie;spp = (s->mstatus >> MSTATUS_SPP_SHIFT) & 1;/* set the IE state to previous IE state */spie = (s->mstatus >> MSTATUS_SPIE_SHIFT) & 1;s->mstatus = (s->mstatus & ~(1 << spp)) |(spie << spp);/* set SPIE to 1 */s->mstatus |= MSTATUS_SPIE;/* set SPP to U */s->mstatus &= ~MSTATUS_SPP;set_priv(s, spp);s->pc = s->sepc;
}

退出中断服务程序后,需要完成以下操作:

  • 恢复mstatus
  • S模式,切换到中断前的模式
  • pc = sepc,跳转中断前的程序PC地址

这些操作,都是做现场恢复(具体参考RISCV规范文档)。

4 总结

中断查询,其流程图,如下所示:
在这里插入图片描述
中断处理,其流程图,如下所示:
在这里插入图片描述

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

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

相关文章

⑤-1 学习PID--什么是PID

​ PID 算法可以用于温度控制、水位控制、飞行姿态控制等领域。后面我们通过PID 控制电机进行说明。 自动控制系统 在直流有刷电机的基础驱动中&#xff0c;如果电机负载不变&#xff0c;我们只要设置固定的占空比&#xff08;电压&#xff09;&#xff0c;电机的速度就会稳定在…

HTML5 新增语义标签及属性

个人主页&#xff1a;学习前端的小z 个人专栏&#xff1a;HTML5和CSS3悦读 本专栏旨在分享记录每日学习的前端知识和学习笔记的归纳总结&#xff0c;欢迎大家在评论区交流讨论&#xff01; 文章目录 ✍HTML5 新增语义标签及属性&#x1f48e;1 HTML5 新增的块级语义化标签&…

使用ADO.NET访问数据库

目录 访问数据库的步骤 &#xff11;、建立数据库 &#xff12;、设置链接参数 &#xff08;1&#xff09;web网页和数据库连接的方法一 &#xff08;2&#xff09;web网页和数据库连接的方法二 &#xff13;、建立链接对象 &#xff14;、显示数据库 &#xff15;、数…

openwrt局域网配置多个IP

在局域网配置过程中&#xff0c;若是DHCP服务器关闭&#xff0c;又忘记了配置的ip&#xff0c;将很难访问到路由器重新进行配置。这种情况可以在路由器出厂时做一个备用ip去避免。 1.配置 以下是备用ip的配置方法&#xff0c;以SKYLAB的SKW99 WIFI模组为例进行说明&#xff1…

Android使用shape属性绘制边框内渐变色

目录 先上效果图实现方法shape属性介绍代码结果 先上效果图 这是使用AndroidStudio绘制的带有渐变色的边框背景色 实现方法 项目中由于UI设计需求&#xff0c;需要给按钮、控件设置带有背景色效果的。以下是UI效果图。 这里我们使用shape属性来绘制背景效果。 shape属性介…

ffmpeg命令与批处理编程

(一) CMD脚本查找所有文件 powershell与cmd转换 powershell与cmd虽然同为windows命令&#xff0c;但许多命令并不通用。 CMD换行符 a 在CMD下&#xff0c;可以用^作为换行符&#xff0c;类似于Linux下的\。举例如下&#xff1a; start pemu.exe ^ -net nic,vlan1,macaddr…

最新版IntelliJ IDEA 2024.1安装和配置教程 详细图文解说版安装教程

IntelliJ IDEA 2024.1 最新版如何快速入门体验?IntelliJ IDEA 2024.1 安装和配置教程 图文解说版 文章目录 IntelliJ IDEA 2024.1 最新版如何快速入门体验?IntelliJ IDEA 2024.1 安装和配置教程 图文解说版前言 第一步&#xff1a; IntelliJ IDEA 2024.1安装教程第 0 步&…

如何通过Linux pciehp sysfs接口控制PCIe Slot电源状态?-2

NVME SSD电源状态判断 通过pciehp sysfs接口对NVMe SSD所在的PCIe插槽进行Power On/Off操作时&#xff0c;确实会间接影响到NVMe SSD本身的电源状态。因为NVMe SSD是作为PCIe设备连接到特定插槽上的&#xff0c;插槽电源状态的变化通常会直接影响到与其相连的设备。 当对PCIe…

Python学习笔记16 - 函数

函数的创建和调用 函数调用的参数传递 函数的返回值 函数的参数定义 变量的作用域 递归函数 斐波那契数列 总结

fiddler常用操作汇总

1、过滤 2、查看数据包内容 3、弱网测试 弱网测试其实就是提前设置好一个值&#xff0c;在这个环境下进行测试就行了。 &#xff08;1&#xff09;进入定制规则页面&#xff1a; (2) 点击CtrlF调起搜索&#xff0c;在Find what 中输入300进行查找&#xff0c;更改上行、下行网…

【Java】SpringBoot快速整合mongoDB

目录 1.什么是mongoDB&#xff1f; 2.Docker安装mongoDB 3.SpringBoot整合mongoDB步骤 4.验证 1.什么是mongoDB&#xff1f; MongoDB是一种非关系型数据库&#xff0c;被广泛用于大型数据存储和分布式系统的构建。MongoDB支持的数据模型比传统的关系型数据库更加灵活&#x…

UDTF函数 explode

场景&#xff1a; 原hive数据形式 split 处理到一个Array 形式 使用explode炸开后的效果是 explode结合侧面视图达到targeType 目标形式&#xff1a; 一进多出 explode 将hive 中复杂的 array 炸成多行 因为炸开后&#xff0c; movie 列值少于categoryname 列所以这里为了达到…

SpringBoot的旅游管理系统+论文+ppt+免费远程调试

项目介绍: 基于SpringBoot旅游网站 旅游管理系统 本旅游管理系统采用的数据库是Mysql&#xff0c;使用SpringBoot框架开发。在设计过程中&#xff0c;充分保证了系统代码的良好可读性、实用性、易扩展性、通用性、便于后期维护、操作方便以及页面简洁等特点。 &#xff08;1&…

《TinyLlama: An Open-Source Small Language Model》全文翻译

【Title】 TinyLlama&#xff1a;开源小语言模型 【Abstract】 我们推出了 TinyLlama&#xff0c;这是一个紧凑的 1.1B 语言模型&#xff0c;在大约 1 万亿个令牌上进行了大约 3 个时期的预训练。 TinyLlama 基于 Llama 2&#xff08;Touvron 等人&#xff0c;2023b&#xff…

【攻防世界】Confusion1

php的标志是大象&#xff0c;Python的标志是蛇 。Python 的 Flask 框架( Flask 使用 Jinja2 作为模板引擎 ) 点进register.php 输入{{3*4}} 输入 {{config}} 也有回显&#xff0c;ssti 判断是否存在ssti注入&#xff1a; 1. {{8*8}} 2. {{config}} 过滤了关键字&#xff0…

动态规划|63.不同路径II

力扣题目链接 class Solution { public:int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {int m obstacleGrid.size();int n obstacleGrid[0].size();if (obstacleGrid[m - 1][n -1] 1 || obstacleGrid[0][0] 1) return 0;vector<ve…

Pygame经典游戏:贪吃蛇

------------★Pygame系列教程★------------ Pygame经典游戏&#xff1a;贪吃蛇 Pygame教程01&#xff1a;初识pygame游戏模块 Pygame教程02&#xff1a;图片的加载缩放旋转显示操作 Pygame教程03&#xff1a;文本显示字体加载transform方法 Pygame教程04&#xff1a;dra…

CameraCtrl、EDTalk、Sketch3D、Diffusion^2、FashionEngine

本文首发于公众号&#xff1a;机器感知 CameraCtrl、EDTalk、Sketch3D、Diffusion^2、FashionEngine NVINS: Robust Visual Inertial Navigation Fused with NeRF-augmented Camera Pose Regressor and Uncertainty Quantification In recent years, Neural Radiance Fields …

【电控笔记0】稳定度判断

简要概括 现控:远离虚轴,稳定度越高 自控:相位裕度PM 增益裕度GM 开环传函 不稳定条件判断

15 - Debian如何配置共享服务Samba

作者&#xff1a;网络傅老师 特别提示&#xff1a;未经作者允许&#xff0c;不得转载任何内容。违者必究&#xff01; Debian如何配置共享服务Samba 《傅老师Debian小知识库系列之15》——原创 前言 傅老师Debian小知识库特点&#xff1a; 1、最小化拆解Debian实用技能&…