湖南大学-操作系统实验四
HUNAN UNIVERSITY
操作系统实验报告
一、实验题目
实验四
中断、异常和陷阱指令是操作系统的基石,现代操作系统就是由中断驱动的。本实验和实验五的目的在于深刻理解中断的原理和机制,掌握CPU访问中断控制器的方法,掌握Arm体系结构的中断机制和规范,实现时钟中断服务和部分异常处理等。
- 实验环境
ubuntu20
- 实验步骤
一、陷入操作系统
如下图所示,操作系统是一个多入口的程序,执行陷阱(Trap)指令,出现异常、发生中断时都会陷入到操作系统。
二、ARMv8的中断与异常处理
ARMv8 架构定义了两种执行状态(Execution States),AArch64 和AArch32。分别对应使用64位宽通用寄存器或32位宽通用寄存器的执行。
上图所示为AArch64中的异常级别(Exception levels)的组织。可见AArch64中共有4个异常级别,分别为EL0,EL1,EL2和EL3。在AArch64中,Interrupt是Exception的子类型,称为异常。 AArch64 中有四种类型的异常:
Sync(Synchronous exceptions,同步异常),在执行时触发的异常,例如在尝试访问不存在的内存地址时。
IRQ (Interrupt requests,中断请求),由外部设备产生的中断
FIQ (Fast Interrupt Requests,快速中断请求),类似于IRQ,但具有更高的优先级,因此 FIQ 中断服务程序不能被其他 IRQ 或 FIQ 中断。
SError (System Error,系统错误),用于外部数据中止的异步中断。
当异常发生时,处理器将执行与该异常对应的异常处理代码。在ARM架构中,这些异常处理代码将会被保存在内存的异常向量表中。每一个异常级别(EL0,EL1,EL2和EL3)都有其对应的异常向量表。需要注意的是,与x86等架构不同,该表包含的是要执行的指令,而不是函数地址 3 。
异常向量表的基地址由VBAR_ELn给出,然后每个表项都有一个从该基地址定义的偏移量。 每个表有16个表项,每个表项的大小为128(0x80)字节(32 条指令)。 该表实际上由4组,每组4个表项组成。 分别是:
发生于当前异常级别的异常且SPSel寄存器选择SP0 4 , Sync、IRQ、FIQ、SError对应的4个异常处理。
发生于当前异常级别的异常且SPSel寄存器选择SPx 4 , Sync、IRQ、FIQ、SError对应的4个异常处理。
发生于较低异常级别的异常且执行状态为AArch64, Sync、IRQ、FIQ、SError对应的4个异常处理。
发生于较低异常级别的异常且执行状态为AArch32, Sync、IRQ、FIQ、SError对应的4个异常处理。
异常向量表
新建 src/bsp/prt_vector.S 文件,参照这里 3 定义异常向量表如下:
- .section .os.vector.text, "ax"
- .global OsVectorTable
- .type OsVectorTable,function
- .align 13
- OsVectorTable:
- .set VBAR, OsVectorTable
- .org VBAR // Synchronous, Current EL with SP_EL0
- EXC_HANDLE 0 OsExcDispatch
- .org (VBAR + 0x80) // IRQ/vIRQ, Current EL with SP_EL0
- EXC_HANDLE 1 OsExcDispatch
- .org (VBAR + 0x100) // FIQ/vFIQ, Current EL with SP_EL0
- EXC_HANDLE 2 OsExcDispatch
- .org (VBAR + 0x180) // SERROR, Current EL with SP_EL0
- EXC_HANDLE 3 OsExcDispatch
- .org (VBAR + 0x200) // Synchronous, Current EL with SP_ELx
- EXC_HANDLE 4 OsExcDispatch
- .org (VBAR + 0x280) // IRQ/vIRQ, Current EL with SP_ELx
- EXC_HANDLE 5 OsExcDispatch
- .org (VBAR + 0x300) // FIQ/vFIQ, Current EL with SP_ELx
- EXC_HANDLE 6 OsExcDispatch
- .org (VBAR + 0x380) // SERROR, Current EL with SP_ELx
- EXC_HANDLE 7 OsExcDispatch
- .org (VBAR + 0x400) // Synchronous, EL changes and the target EL is using AArch64
- EXC_HANDLE 8 OsExcDispatchFromLowEl
- .org (VBAR + 0x480) // IRQ/vIRQ, EL changes and the target EL is using AArch64
- EXC_HANDLE 9 OsExcDispatch
- .org (VBAR + 0x500) // FIQ/vFIQ, EL changes and the target EL is using AArch64
- EXC_HANDLE 10 OsExcDispatch
- .org (VBAR + 0x580) // SERROR, EL changes and the target EL is using AArch64
- EXC_HANDLE 11 OsExcDispatch
- .org (VBAR + 0x600) // Synchronous, L changes and the target EL is using AArch32
- EXC_HANDLE 12 OsExcDispatch
- .org (VBAR + 0x680) // IRQ/vIRQ, EL changes and the target EL is using AArch32
- EXC_HANDLE 13 OsExcDispatch
- .org (VBAR + 0x700) // FIQ/vFIQ, EL changes and the target EL is using AArch32
- EXC_HANDLE 14 OsExcDispatch
- .org (VBAR + 0x780) // SERROR, EL changes and the target EL is using AArch32
- EXC_HANDLE 15 OsExcDispatch
- .text
可以看到:针对4组,每组4类异常共16类异常均定义有其对应的入口,且其入口均定义为 EXC_HANDLE vecId handler 的形式。
在 prt_reset_vector.S 中的 OsEnterMain: 标号后加入代码
- OsVectTblInit: // 设置 EL1 级别的异常向量表
- LDR x0, =OsVectorTable
- MSR VBAR_EL1, X0
上下文保存与恢复
EXC_HANDLE 实际上是一个宏,其定义如下。
- .global OsExcHandleEntry
- .type OsExcHandleEntry, function
- .macro SAVE_EXC_REGS // 保存通用寄存器的值到栈中
- stp x1, x0, [sp,#-16]!
- stp x3, x2, [sp,#-16]!
- stp x5, x4, [sp,#-16]!
- stp x7, x6, [sp,#-16]!
- stp x9, x8, [sp,#-16]!
- stp x11, x10, [sp,#-16]!
- stp x13, x12, [sp,#-16]!
- stp x15, x14, [sp,#-16]!
- stp x17, x16, [sp,#-16]!
- stp x19, x18, [sp,#-16]!
- stp x21, x20, [sp,#-16]!
- stp x23, x22, [sp,#-16]!
- stp x25, x24, [sp,#-16]!
- stp x27, x26, [sp,#-16]!
- stp x29, x28, [sp,#-16]!
- stp xzr, x30, [sp,#-16]!
- .endm
- .macro RESTORE_EXC_REGS // 从栈中恢复通用寄存器的值
- ldp xzr, x30, [sp],#16
- ldp x29, x28, [sp],#16
- ldp x27, x26, [sp],#16
- ldp x25, x24, [sp],#16
- ldp x23, x22, [sp],#16
- ldp x21, x20, [sp],#16
- ldp x19, x18, [sp],#16
- ldp x17, x16, [sp],#16
- ldp x15, x14, [sp],#16
- ldp x13, x12, [sp],#16
- ldp x11, x10, [sp],#16
- ldp x9, x8, [sp],#16
- ldp x7, x6, [sp],#16
- ldp x5, x4, [sp],#16
- ldp x3, x2, [sp],#16
- ldp x1, x0, [sp],#16
- .endm
- .macro EXC_HANDLE vecId handler
- SAVE_EXC_REGS // 保存寄存器宏
- mov x1, #\vecId // x1 记录异常类型
- b \handler // 跳转到异常处理
- .endm
EXC_HANDLE 宏的主要作用是一发生异常就立即保存CPU寄存器的值,然后跳转到异常处理函数进行异常处理。随后,我们继续在 src/bsp/prt_vector.S 文件中实现异常处理函数,包括 OsExcDispatch和OsExcDispatchFromLowEl。
- .global OsExcHandleEntry
- .type OsExcHandleEntry, function
- .global OsExcHandleEntryFromLowEl
- .type OsExcHandleEntryFromLowEl, function
- .section .os.init.text, "ax"
- .globl OsExcDispatch
- .type OsExcDispatch, @function
- .align 4
- OsExcDispatch:
- mrs x5, esr_el1
- mrs x4, far_el1
- mrs x3, spsr_el1
- mrs x2, elr_el1
- stp x4, x5, [sp,#-16]!
- stp x2, x3, [sp,#-16]!
- mov x0, x1 // x0: 异常类型
- mov x1, sp // x1: 栈指针
- bl OsExcHandleEntry // 跳转到实际的 C 处理函数, x0, x1分别为该函数的第1,2个参数。
- ldp x2, x3, [sp],#16
- add sp, sp, #16 // 跳过far, esr, HCR_EL2.TRVM==1的时候,EL1不能写far, esr
- msr spsr_el1, x3
- msr elr_el1, x2
- dsb sy
- isb
- RESTORE_EXC_REGS // 恢复上下文
- eret //从异常返回
- .globl OsExcDispatchFromLowEl
- .type OsExcDispatchFromLowEl, @function
- .align 4
- OsExcDispatchFromLowEl:
- mrs x5, esr_el1
- mrs x4, far_el1
- mrs x3, spsr_el1
- mrs x2, elr_el1
- stp x4, x5, [sp,#-16]!
- stp x2, x3, [sp,#-16]!
- mov x0, x1
- mov x1, sp
- bl OsExcHandleFromLowElEntry
- ldp x2, x3, [sp],#16
- add sp, sp, #16 // 跳过far, esr, HCR_EL2.TRVM==1的时候,EL1不能写far, esr
- msr spsr_el1, x3
- msr elr_el1, x2
- dsb sy
- isb
- RESTORE_EXC_REGS // 恢复上下文
- eret //从异常返回
OsExcDispatch 首先保存了4个系统寄存器到栈中,然后调用实际的异常处理 OsExcHandleEntry 函数。当执行完 OsExcHandleEntry 函数后,我们需要依序恢复寄存器的值。这就是操作系统课程中重点讲述的上下文的保存和恢复过程。
OsExcDispatchFromLowEl 与 OsExcDispatch 的操作除调用的实际异常处理函数不同外其它完全一致。
异常处理函数
新建 src/bsp/prt_exc.c 文件,实现实际的 OsExcHandleEntry 和 OsExcHandleFromLowElEntry 异常处理函数。
- #include "prt_typedef.h"
- #include "os_exc_armv8.h"
- extern U32 PRT_Printf(const char *format, ...);
- // ExcRegInfo 格式与 OsExcDispatch 中寄存器存储顺序对应
- void OsExcHandleEntry(U32 excType, struct ExcRegInfo *excRegs)
- {
- PRT_Printf("Catch a exception.\n");
- }
- // ExcRegInfo 格式与 OsExcDispatchFromLowEl 中寄存器存储顺序对应
- void OsExcHandleFromLowElEntry(U32 excType, struct ExcRegInfo *excRegs)
- {
- PRT_Printf("Catch a exception from low exception level.\n");
- }
注意到上面两个异常处理函数的第2个参数是 struct ExcRegInfo * 类型,而在 src/bsp/prt_vector.S 中我们为该参数传递是栈指针 sp。所以该结构需与异常处理寄存器保存的顺序保持一致。新建 src/bsp/os_exc_armv8.h 文件,定义 ExcRegInfo 结构。
- #ifndef ARMV8_EXC_H
- #define ARMV8_EXC_H
- #include "prt_typedef.h"
- #define XREGS_NUM 31
- struct ExcRegInfo {
- // 以下字段的内存布局与TskContext保持一致
- uintptr_t elr; // 返回地址
- uintptr_t spsr;
- uintptr_t far;
- uintptr_t esr;
- uintptr_t xzr;
- uintptr_t xregs[XREGS_NUM]; // 0~30 : x30~x0
- };
- #endif /* ARMV8_EXC_H */
触发异常
释掉 FPU 启用代码,构建系统并执行发现没有任何信息输出,通过调试将会观察到异常。
系统调用
系统调用是通用操作系统为应用程序提供服务的方式,理解系统调用对理解通用操作系统的实现非常重要。下面我们来实现1条简单的系统调用。
EL 0 是用户程序所在的级别,而在lab1中我们已经知道CPU启动后进入的是EL1或以上级别。
在 main 函数中我们首先返回到 EL0 级别,然后通过 SVC 调用一条系统调用.
- #include "prt_typedef.h"
- extern U32 PRT_Printf(const char *format, ...);
- extern void PRT_UartInit(void);
- S32 main(void)
- {
- const char Test_SVC_str[] = "Hello, my first system call!";
- PRT_UartInit();
- PRT_Printf(" ##### \n");
- PRT_Printf(" # # ###### # # #### # # # # # \n");
- PRT_Printf(" # # ## # # # # # ## # \n");
- PRT_Printf(" # #### ##### # # # #### ###### # # # # \n");
- PRT_Printf(" # # # # # # # # # # # # # \n");
- PRT_Printf(" # # # # # # # # # # # # # # \n");
- PRT_Printf(" ##### ###### # # #### # # # # # \n");
- PRT_Printf("ctr-a h: print help of qemu emulator. ctr-a x: quit emulator.\n\n");
- // 回到异常 EL 0级别,模拟系统调用,查看异常的处理,了解系统调用实现机制。
- // 《Bare-metal Boot Code for ARMv8-A Processors》
- OS_EMBED_ASM(
- "MOV X1, #0b00000\n" // Determine the EL0 Execution state.
- "MSR SPSR_EL1, X1\n"
- "ADR x1, EL0Entry\n" // Points to the first instruction of EL0 code
- " MSR ELR_EL1, X1\n"
- "eret\n" // 返回到 EL 0 级别
- "EL0Entry: \n"
- "MOV x0, %0 \n" //参数1
- "MOV x8, #1\n" //在linux中,用x8传递 syscall number,保持一致。
- "SVC 0\n" // 系统调用
- "B .\n" // 死循环,以上代码只用于演示,EL0级别的栈未正确设置
- ::"r"(&Test_SVC_str[0])
- );
- // 在 EL1 级别上模拟系统调用
- // OS_EMBED_ASM("SVC 0");
- return 0;
- }
系统调用实现
在 src/bsp/prt_exc.c 修改 OsExcHandleFromLowElEntry 函数实现 1 条系统调用。
- extern void TryPutc(unsigned char ch);
- void MyFirstSyscall(char *str)
- {
- while (*str != '\0') {
- TryPutc(*str);
- str++;
- }
- }
- // ExcRegInfo 格式与 OsExcDispatch 中寄存器存储顺序对应
- void OsExcHandleFromLowElEntry(U32 excType, struct ExcRegInfo *excRegs)
- {
- int ExcClass = (excRegs->esr&0xfc000000)>>26;
- if (ExcClass == 0x15){ //SVC instruction execution in AArch64 state.
- PRT_Printf("Catch a SVC call.\n");
- // syscall number存在x8寄存器中, x0为参数1
- int syscall_num = excRegs->xregs[(XREGS_NUM - 1)- 8]; //uniproton存储的顺序x0在高,x30在低
- uintptr_t param0 = excRegs->xregs[(XREGS_NUM - 1)- 0];
- PRT_Printf("syscall number: %d, param 0: 0x%x\n", syscall_num, param0);
- switch(syscall_num){
- case 1:
- MyFirstSyscall((void *)param0);
- break;
- default:
- PRT_Printf("Unimplemented syscall.\n");
- }
- }else{
- PRT_Printf("Catch a exception.\n");
- }
- }
这段代码是一个异常处理程序和系统调用的实现。它展示了如何在异常处理过程中识别和处理系统调用。我们逐行分析代码:
- extern void TryPutc(unsigned char ch);:
- 声明了一个外部函数 TryPutc,用于输出一个字符。这意味着函数的定义在别处,但在这里被引用。
- void MyFirstSyscall(char *str):
- 声明了一个名为 MyFirstSyscall 的函数,该函数接受一个字符串指针作为参数。
- {:
- 函数体的开始。
- while (*str != '\0') {:
- 当字符串未结束时(即当前字符不是空字符 '\0'),继续循环。
- TryPutc(*str);:
- 调用 TryPutc 函数输出当前字符。
- str++;:
- 移动指针到下一个字符。
- }:
- 循环结束。
- }:
- MyFirstSyscall 函数结束。
- // ExcRegInfo 格式与 OsExcDispatch 中寄存器存储顺序对应:
- 注释,解释 ExcRegInfo 结构的格式与 OsExcDispatch 中寄存器存储顺序相对应。
- void OsExcHandleFromLowElEntry(U32 excType, struct ExcRegInfo *excRegs):
- 声明了一个名为 OsExcHandleFromLowElEntry 的函数,该函数接受一个异常类型和指向寄存器信息结构的指针。
- {:
- 函数体的开始。
- int ExcClass = (excRegs->esr & 0xfc000000) >> 26;:
- 提取异常类(Exception Class)字段,该字段位于异常状态寄存器(ESR)的高位。
- if (ExcClass == 0x15){ // SVC instruction execution in AArch64 state.:
- 检查异常类是否为0x15(SVC指令执行)。
- PRT_Printf("Catch a SVC call.\n");:
- 打印捕获到SVC调用的消息。
- // syscall number存在x8寄存器中, x0为参数1:
- 注释,解释系统调用号在 x8 寄存器中,x0 为参数1。
- int syscall_num = excRegs->xregs[(XREGS_NUM - 1) - 8];:
- 从寄存器数组中提取系统调用号,假设 x0 在高位,x30 在低位。
- uintptr_t param0 = excRegs->xregs[(XREGS_NUM - 1) - 0];:
- 从寄存器数组中提取第一个参数。
- PRT_Printf("syscall number: %d, param 0: 0x%x\n", syscall_num, param0);:
- 打印系统调用号和第一个参数。
- 空行,分隔代码块。
- switch(syscall_num){:
- 开始处理系统调用号的 switch 语句。
- case 1::
- 如果系统调用号为1。
- MyFirstSyscall((void *)param0);:
- 调用 MyFirstSyscall 函数,传递第一个参数。
- break;:
- 跳出 switch 语句。
- default::
- 默认情况(未实现的系统调用)。
- PRT_Printf("Unimplemented syscall.\n");:
- 打印未实现的系统调用的消息。
- }:
- 结束 switch 语句。
- } else {:
- 如果异常类不是0x15。
- PRT_Printf("Catch a exception.\n");:
- 打印捕获到异常的消息。
- 空行,分隔代码块。
- }:
- 结束 if-else 语句。
- }:
- OsExcHandleFromLowElEntry 函数结束。
总结:
- MyFirstSyscall 函数负责输出字符串。
- OsExcHandleFromLowElEntry 函数负责处理异常,特别是识别和处理 SVC 调用(系统调用)。
- 当捕获到 SVC 调用时,函数提取系统调用号和参数,调用相应的系统调用处理函数。
- Lab4作业
作业
查找 启用FPU前异常出现的位置和原因。禁用FPU后PRT_Printf工作不正常,需通过调试跟踪查看异常发生的位置和原因elr_el1 esr_el1寄存器。
首先注释FPU,进入GDB调试:
进入异常向量表:
查看到ELR_EL1寄存器的值为0x400021e8,ESR_EL1寄存器的值为0x1fe0000。
五、总结
在实验过程中,对于 UniProton 系统更加熟悉了,并且通过自己去github 上查看了 libboundscheck 库,其选取并实现了常见的内存/字符串操作类的函数,如 memcpy_s、strcpy_s 等函数,并且处理边界检查函数的版本发布、更新以及维护,了解了这些开源项目的下载和如何自己去一步一步运行。
相关文章:
湖南大学-操作系统实验四
HUNAN UNIVERSITY 操作系统实验报告 一、实验题目 实验四 中断、异常和陷阱指令是操作系统的基石,现代操作系统就是由中断驱动的。本实验和实验五的目的在于深刻理解中断的原理和机制,掌握CPU访问中断控制器的方法,掌握Arm体系结构的中断机…...
3.第三章:数据治理的战略价值
文章目录 3.1 数据治理与企业战略3.1.1 数据驱动的决策体系3.1.2 数据资产的价值挖掘3.1.3 风险防控与合规管理 3.2 数据治理的业务价值3.2.1 提升客户体验3.2.2 优化运营效率3.2.3 加速产品创新 3.3 数据治理的技术价值3.3.1 提升数据质量3.3.2 优化数据架构3.3.3 增强系统集成…...
[KVM] KVM挂起状态恢复失败与KVM存储池迁移
背景:发现KVM host上的几台虚拟机挂起了(paused),但是并没有执行virsh suspend <vm_hostname>,且使用virsh resume <vm_hostname> 无法恢复。原因是这个几个虚拟机归属的存储池所在的磁盘满了。所以想把虚拟机迁移到磁盘空间富余…...
图文结合 - 光伏系统产品设计PRD文档 -(慧哥)慧知开源充电桩平台
光伏系统产品设计PRD文档 版本号:1.0 修订日期:2023年10月 作者: 一、文档概述 1.1 背景与目标 行业背景:全球光伏装机量年增长20%,数字化运维需求迫切用户痛点:现有系统存在数据延…...
linux-相关命令
一、Linux 详细介绍 1. 什么是 Linux? Linux 是一个开源的类 Unix 操作系统,其核心是 Linux 内核。它最早由 Linus Torvalds 在 1991 年发布,后来逐渐发展成各种发行版(如 Ubuntu、CentOS、Debian、Arch 等)。 2. L…...
Hive中Map和Reduce阶段的分工
在Hive查询执行过程中,Map和Reduce阶段有明确的分工,但实际情况要复杂一些。 基本分工原则 Map阶段: 主要职责是读取输入数据并进行初步处理输出键值对形式的数据Reduce阶段: 接收Map阶段输出的键值对对相同键的值进行聚合/计算输出最终结果实际执行中的复…...
前端笔记-Vue router
学习目标 Vue Router路由管理1、路由配置2、嵌套路由3、路由守卫与权限控制 一、路由配置(给网站做地图) npm i vue-router 作用:告诉浏览器什么地址该显示什么页面 核心代码: // 创建路由并暴露出去// 第一步&#x…...
MySQL的日志--Redo Log【学习笔记】
MySQL的日志--Redo Log 知识来源: 《MySQL是怎样运行的》--- 小孩子4919 MySQL的事务四大特性之一就是持久性(Durability)。但是底层是如何实现的呢?这就需要我们的Redo Log(重做日志)闪亮登场了。它记录着…...
《系统分析师-第三阶段—总结(五)》
背景 采用三遍读书法进行阅读,此阶段是第三遍。 过程 第9章 总结 在这个过程中,对导图的规范越来越清楚,开始结构化,找关系,找联系。...
【LangChain4j】AI 第二弹:项目中接入 LangChain4j
普通接入方式 参考文档: Get Started https://docs.langchain4j.dev/get-started 1.添加依赖 <!-- 基于open-ai的langchain4j接口:ChatGPT、deepseek都是open-ai标准下的大模型 --> <dependency><groupId>dev.langchain4j</grou…...
测试基础笔记第十天
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 一、查询语句1.基本查询2.条件查询3.模糊查询4.范围查询5.判断空 二、其他复杂查询1.排序2.聚合函数3.分组4.分页查询 一、查询语句 1.基本查询 – 需求1: 准备商…...
代理模式:控制对象访问的中间层设计
代理模式:控制对象访问的中间层设计 一、模式核心:通过代理对象控制对目标对象的访问 在软件开发中,有时需要为对象添加一个 “代理” 来控制对它的访问,例如: 远程代理:访问远程对象时(如 R…...
Python类和对象二(十一)
构造函数: 重写: 通过类名访问类里面的方法的做法,称为调用未绑定的父类方法,他有时候会产生钻石继承问题: 发现A重复初始化了两次,类c同事继承类B1和B2,类B1和B2又是继承类A的,当c…...
大模型在代码安全检测中的应用
大模型在代码安全检测领域的应用近年来取得显著进展,尤其在代码审查(Code Review, CR)场景中展现出高效性与准确性。以下是其核心优势、技术路径、挑战及实践案例的总结: 一、技术优势与核心能力 语义理解与上下文分析 大模型通过…...
Python实现图片浏览器
Python实现图片浏览器 支持浏览多种常见图片格式:JPG, JPEG, PNG, GIF, BMP, TIFF, WEBP 通过"打开文件夹"按钮选择任何包含图片的文件夹 灵活的排序选项: 按时间排序(新→旧或旧→新) 按文件名排序(A→…...
网页设计规范:从布局到交互的全方位指南
网页设计规范看似繁杂,但其实都是为了给用户提供更好的体验。只有遵循这些规范,才能设计出既美观又实用的网页,让用户在浏览网页时感到舒适、愉悦。 一、用户体验至上 用户体验(UX)是网页设计的核心原则之一。设计师…...
哪些心电图表现无缘事业编体检呢?
根据《公务员录用体检通用标准》心血管系统条款及事业单位体检实施细则,心电图不合格主要涉及以下类型及处置方案: 一、心律失常类 早搏:包括房性早搏、室性早搏和交界性早搏。如果每分钟早搏次数较多(如超过5次)&…...
Java基础系列-HashMap源码解析1-BST树
文章目录 序二叉搜索树(BST)引入查找5插入9极端情况删除删除叶节点 10删除节点只有左子树或只有右子树删除节点既有左子树又有右子树为什么这么代替? 序 提到HashMap,就不得不提红黑树(HashMap1.8之后)&am…...
生物计算安全攻防战:从DNA存储破译到碳基芯片防御体系重构
随着碳基生物芯片突破冯诺依曼架构限制,DNA数据存储密度达到1EB/克量级,合成生物学与信息技术的融合正引发新一轮安全革命。本文深入解析碳基芯片逆向工程路径,揭示酶驱动DNA数据解码的技术突破,预警合成生物回路潜在的数据泄露风…...
【金仓数据库征文】从Oracle到KingbaseES的语法兼容与迁移
随着“信创”战略的深入推进,国产数据库逐渐成为IT系统的重要组成部分。KingbaseES(金仓数据库)凭借其良好的Oracle兼容性和日益完善的生态,成为金融、政务等核心行业国产化替代的重要选项。本文将从语法兼容性分析出发࿰…...
MATLAB 下载安装教程
## 一、下载MATLAB 1. 访问 MathWorks 官方网站:https://www.mathworks.com/ 2. 点击右上角的"登录"按钮 - 如果没有账号,需要先注册一个 MathWorks 账号 - 学生可以使用教育邮箱注册,获得教育版授权 3. 登录后,点击&…...
Android kotlin通知功能完整实现指南:从基础到高级功能
本文将详细介绍如何在Android应用中实现通知功能,包括基础通知、动作按钮和内联回复等高级特性。 一、基础通知实现 1. 基本通知发送方法 fun sendBasicNotification(context: Context, title: String, message: String) {// 1. 创建通知渠道(Android 8.0必需)va…...
Javase 基础入门 —— 04 继承
本系列为笔者学习Javase的课堂笔记,视频资源为B站黑马程序员出品的《黑马程序员JavaAI智能辅助编程全套视频教程,java零基础入门到大牛一套通关》,章节分布参考视频教程,为同样学习Javase系列课程的同学们提供参考。 01 什么是继…...
2.4/Q2,Charls最新文章解读
文章题目:The impact of hearing ability on depression among retired middle-aged and elderly individuals in China: the chain mediating role of self-rated health and life satisfaction DOI:10.1186/s41043-025-00791-9 中文标题:中…...
对流对象的理解
在c里,“流”可以理解为数据传输与操作的“介质”。 从输入输出角度来看,有输入流(比如cin)和输出流(cout)。对于输入流,数据通过它从外部设备(例如键盘)“流入”程序内…...
RBAC权限-笔记
1. RBAC模型简介 1.1. RBAC三要素 RBAC权限模型(Role-Based Access Control:基于角色的访问控制)有3个基础组成部分,分别是:用户、角色和权限。它们之间的关系如下图所示: 用户(User)…...
stm32之GPIO函数详解和上机实验
目录 1.LED和蜂鸣器1.1 LED1.2 蜂鸣器 2.实验2.1 库函数:RCC和GPIO2.1.1 RCC函数1. RCC_AHBPeriphClockCmd2. RCC_APB2PeriphClockCmd3. RCC_APB1PeriphClockCmd 2.1.2 GPIO函数1. GPIO_DeInit2. GPIO_AFIODeInit3. GPIO_Init4. GPIO_StructInit5. GPIO_ReadInputDa…...
MsQuick编译和使用
MsQuick编译和使用 编译克隆代码使用cmakevs2022编译 使用示例 编译 克隆代码 git clone --recurse-submodules https://github.com/microsoft/msquic.git使用cmakevs2022编译 然后直接configure之后Generate然后打开vs工程编译即可生成动态库 使用示例 #include <s…...
01 ubuntu中wps桌面快捷键无法使用
文章目录 1. 问题描述:2. 解决方法:3. 结果展示4. 参考 1. 问题描述: 2. 解决方法: 添加权限 chmod 755 ./wps-office-prometheus.desktop 右键选择允许运行 3. 结果展示 修改前 修改后 4. 参考 参考1...
云原生后端架构:重塑后端开发的新范式
📝个人主页🌹:慌ZHANG-CSDN博客 🌹🌹期待您的关注 🌹🌹 一、引言:后端开发的新时代正在到来 传统的后端开发常常面临如下挑战:部署流程复杂、环境不一致、系统难以扩展、监控能力薄弱、上线流程缓慢。在企业数字化转型、业务快速迭代的大背景下,这些问题暴露得…...
Linux命令-tcpdump
tcpdump 是一个功能强大的网络数据包捕获和分析工具。以下是 tcpdump 命令的完整参数列表及说明: 参数 -a 将网络地址和广播地址转换为名字 tcpdump -a -i eth0-A 以 ASCII 格式打印所有分组,最小化链路层头部信息 tcpdump -A-b 在数据链路层上选择协议…...
分糖果——牛客
链接:登录—专业IT笔试面试备考平台_牛客网 来源:牛客网 题目描述 幼儿园准备了nnn包糖果,每包糖果里有111、222或333颗美味的糖果。现在需要将这些这些糖果平分给两个表现优异的小朋友以作奖励,为了公平公正,需要…...
L0、L2和L∞范数这三种范数的区别
目录 一、解释 1. L0范数:数一数你有多少件行李 2. L2范数:别把行李塞得太满 3. L∞范数:别带任何超重的东西 一句话总结 二、作用 1. L0范数的作用:做减法,只留最重要的…...
[实战]zynq7000设备树自动导出GPIO
目录 zynq7000设备树自动导出GPIO添加设备树节点验证实验 结论 zynq7000设备树自动导出GPIO 今天无聊,掏出我82年产的microzed玩一玩。玩啥好呢,要不点个灯吧。于是,三下五除二,通过linux sys接口以及echo,很快就点亮…...
java六人打分
import java.util.Scanner;public class HelloWorld {public static void main(String[] args) {//打分平均分System.out.println("请输入六个评分");Scanner sc new Scanner(System.in);double[] score new double[6];for(int i0;i<score.length;i){System.ou…...
量子计算浪潮下的安全应对之法
量子计算凭借其强大的计算能力,被传言能够在极短时间内秒级破解传统计算机需耗时漫长岁月(以万年算)才能解开的密码,成为了近年来人们讨论的热点。这看似高深的科技名词在网络安全中又扮演着何种角色?我们应从当前人们…...
Windows Server 2022 常见问题解答
一、安装与部署 1.1 系统要求 硬件配置:最低需要 1.4 GHz 64 位处理器、512 MB 内存、32 GB 硬盘空间。但在实际生产环境中,为确保系统流畅运行,建议使用 2.0 GHz 以上处理器、8 GB 以上内存和 100 GB 以上硬盘。软件兼容性:与大多数基于 Windows 的企业级应用兼容,但在安…...
项目组合管理PPM
项目组合管理(Project Portfolio Management, PPM)详述 一、定义与核心目标 定义 项目组合管理是通过系统化的方法,对组织的所有项目和项目集进行识别、选择、优先级排序、资源配置和动态监控,以确保其与战略目标一致,并最大化投资回报(ROI)的管理过程。 核心目标 战略…...
自建开源远程协助服务RustDesk —— 筑梦之路
开源项目 # 服务端https://github.com/rustdesk/rustdesk-server.git# 客户端https://github.com/rustdesk/rustdesk.git 搭建服务端 需要使用的端口、协议 hbbs - RustDesk ID 注册服务器 hbbr - RustDesk 中继服务器默认情况下,hbbs 监听 21115(tcp) , 21…...
【android bluetooth 协议分析 11】【AVDTP详解 2】【avdtp 初始化阶段主要回调关系梳理】
在车机中 a2dp 通常情况下作为 sink. 本篇来帮助各位 朋友梳理一下,这部分的初始化流程。 我们着重梳理 native 层的逻辑, framework - java 侧一般比较容易看明白, 暂时不做梳理。 如果需要笨叔梳理的可以在博客评论。 出专门的章节来梳理。…...
C++回顾 day3
宏定义的数据是在预处理发生了替换 const类型的数据是在编译阶段发生的替换 命名空间 namespace 空间名{int a;void func_print(){printf("func_print");}struct Stu{int x;char *y;};//或者其他命名空间 } Space::x 20; cout << Space::x;using Space::x;…...
机器学习算法-分类决策树
分类决策树算法-python实现 数据集 具体方法是:从根结点开始,对结点计算所有可能的特征的信息增益,选择信息增益最大的特征作为结点的特征,由该特征的不同取值建立子节点;再对子结点递归地调用以上方法,构…...
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(9): 意向形
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(9): 意向形 1、前言(1)情况说明(2)工程师的信仰 2、知识点(1)「~てしまう」=「~ちゃう…...
kotlin与MVVM结合使用总结(一)
一、Kotlin 与 MVVM 结合的核心优势 代码简洁性 数据类(data class)简化 Model 层定义,自动生成equals/hashCode/toString扩展函数简化 View 层逻辑(如点击事件扩展)lateinit/by lazy优化 ViewModel 属性初始化 异步处…...
达妙电机CAN通信及实验
项目进一步往下做的时候,要上实物了,需要用到达妙电机,虽然有说明书和例程,但是STM32控制电机的具体时间还是花了些时间,我的板子和例程的有些区别,中间很多地方都需要进行修改完善,而且还补充了…...
语音合成之四基于LLM的语音合成
基于LLM的语音合成 1.技术架构1.1 LlaSA1.2 CosyVoice (和 CosyVoice2)1.3 SparkTTS 2 特性对比2.1 零样本语音克隆2.2 多语种支持2.3 可控语音生成2.4 计算效率和模型大小 总结 当前,在大型语言模型(Large Language Models,LLMs)…...
Docker Python 官方镜像使用说明(TAG说明)
Docker Python 官方镜像使用说明(TAG说明) 本文将以python的3.12版本,详细讲解官方 Python 镜像 的TAGS含义 官方文档:https://github.com/tuonioooo/docker 🧭 一张图先看懂(最常见 Tag) py…...
Node.js 开发用户登录功能(使用mysql实现)
在 Web 开发中,用户登录功能是一个基础且重要的部分。、 一、环境搭建 在开始开发之前,我们需要搭建好相应的开发环境。以下是所需的工具和库: Node.js:作为 JavaScript 的运行环境,为我们的项目提供支持。mysql2&am…...
程序员学英文之Shipment Claim 运输和索赔
Time is precious , don’t waste your time, you should spend your time on something valuable . 时间很宝贵,不要浪费时间,你应该把时间用在有 价值的事情上。 Dia-1: Shipment by Voyage Charter 租船装运 1. May I know when your bo…...
python实战项目64:selenium采集软科中国大学排名数据
python实战项目64:selenium采集软科中国大学排名数据 一、项目需求二、流程分析三、完整代码一、项目需求 本项目的需求是使用selenium采集软科中国大学排名的数据。网站首页如下: 抓取此网页数据一般有两种方式,一种是直接发requests请求,我们这里采用的是使用selenium控…...