RT_Thread内核源码分析(一)——CM3内核和上下文切换
目录
一、程序存储分析
1.1 CM3内核寻址空间映射
1.2 程序静态存储和动态执行
二、CM3内核相关知识
2.1 操作模式和特权极别
2.2 环境相关寄存器
2.2.1 通用寄存器组,
2.2.2 状态寄存器组
2.2.3 模式切换环境自动保存
2.2.4 函数调用形参位置
2.3 中断
2.3.1 PendSV(可悬起软中断)
2.3.2 SysTick(系统定时器),
三、RT_Thread线程结构
四、RT_Thread启动分析
五、RT_Thread上线文切换
5.1 上下文切换本质
5.2 上下文切换场景
5.3 上下文切换源码实现
5.3.1 启动第一个线程
5.3.2 调度中上下文切换
5.3.3 上下文切换源码分析
5.3.4 上下文切换逻辑图
5.4 上下文切换实例分析
对于实时操作系统来说,上下文切换是线程抢占或轮询的根本,要想吃透操作系统,必需先搞明白上下文切换的过程,若要搞明白上线文切换,必然少不了与CPU内核打交道,本文基于STM32F10X系列单片机( 内核为Cortex-M3核,简称CM3),讲解RT_Thread Nano实时操作系统的上下文切换。
本章基于RT_Thread Nano V3.1.5版本分析
本章基于Cortex-M3内核和Keil5编译器分析,详细内核知识参考《CM3权威指南CnR2 宋岩 译》
一、程序存储分析
1.1 CM3内核寻址空间映射
如下图所示:
对于STM32F103ZE型号的单片机,Code区采用容量为512kB总线接口的NorFlash;SRAM区为64kB的SRAM存储器,暂不考虑扩展SRAM。
1.2 程序静态存储和动态执行
使用keil5编译STM32程序后,程序存储分析如下图所示:
各存储区说明:
存储区 | 属性 | 存储位置 | 运行位置 | 存储内容 | |
CODE | 只读 | NorFlash | NorFlash | 代码区:所有程序指令。 (起始为MSP初始化值+Reset向量) | |
RO-DATA | 只读 | NorFlash | NorFlash | 常量区:const修饰的常量、字符串。 | |
RW-DATA | 读/写 | NorFlash | SRAM | 变量区:初始化为非0的全局、静态变量。 | |
ZI-DATA | 变量区 | 读/写 | 无 | SRAM (运行中分配) | 变量区:未初始化和初始化为0的全局、静态变量。 |
heap | 读/写 | 无 | SRAM (运行中分配) | 堆区:使用编译器微库,用于malloc申请动态内存。 | |
stack | 读/写 | 无 | SRAM (运行中分配) | 栈区:特权级线程(Thread)、中断(异常)模式的局部变量。 |
(1)单片机内部FLASH启动模式下,ICode总线只能从NorFLASH取指令,代码段只能在NorFlash运行。
(2)总线接口的NorFlash可以用作只读内存,RO-DATA(只读数据)可以不搬移至内存。
(2)heap段使用需要启动keil的微库,才能通过malloc等操作进行动态内存申请,操作系统可以使用该方式分配总的动态内存区,但不要使用该方式分配任务栈,任务栈申请使用操作系统自带的方式。
(3)stack段默认给特权级线程、中断(异常)模式使用,由MSP寄存器控制出入栈,如果切换到用户模式,自动切换为进程栈指针寄存器(PSP),用户模式需要重新申请栈区,PSP指向用户栈区;操作系统中的动态内存区可以根据需求设置在RW-DATA段、ZI-DATA堆区、ZI-DATA变量区。
(4)ZI-DATA区:该类变量初始值全为0,故不体现在静态存储中,在运行态由指令进行内存分配,分配代码由Keil编译器自动生成,我们只需要在工程配置和启动文件中将存储器参数和堆栈申请空间配置好就行,如下所示:
Keil编译器存储器设置NorFLASH、SRAM:
启动文件(.s)中主程序堆(heap)空间分配:
启动文件(.s)中主程序栈(stack)空间分配:
编译结果:
Map文件查看:
二、CM3内核相关知识
2.1 操作模式和特权极别
- CM3有2种操作模式: 处理者模式(或中断(异常)模式 handler mode )、线程(Thread mode)模式。
- CM3有2种权利级别: 特权级、用户级,特权级使用MSP栈指针寄存器,用户级使用PSP栈指针寄存器。两种级别的栈相互独立。
- 处理者模式(handler mode)只能运行在特权级别。
- 用户级对系统控制空间(SCS)的访问将被阻止——该空间包含了配置寄存器组以及调试组件的寄存器组。还禁止使用 MRS/MSR 访问,除了 APSR 之外的特殊功能寄存器。如果以身试法,则对于访问特殊功能寄存器的,访问操作被忽略;而对于访问 SCS 空间的,将触发fault异常。
- 软件触发中断寄存器可以在用户级下访问以产生软件中断(利用这个特性实现用户模式到特权模式转变)。
各种模式之间的切换:
模式切换 | 触发条件 | 栈指针 |
特权级Thread 模式 | 上电启动 | MSP |
特权级Thread 模式->特权级handler模式 | 中断、异常触发 R14(LD)更新为0XFFFFFFF9 | MSP |
特权级Thread 模式->用户级Thread 模式 | 操作寄存器CONTROL[0]置1 | MSP切换为PSP |
特权级handler模式->特权级Thread 模式 | 调用指令“BX R14” 其中R14=0XFFFFFFF9 | MSP |
特权级handler模式->用户级Thread 模式 | 调用指令“BX R14” 其中R14=0XFFFFFFFD | MSP切换为PSP |
用户级Thread 模式->特权级handler模式 | 中断、异常触发 R14更新为0XFFFFFFFD | PSP切换为MSP |
用户级Thread 模式->特权级Thread 模式 | 主动触发异常(SVCall异常) 调用指令“BX R14”返回异常 其中R14=0XFFFFFFF9 | PSP切换为MSP |
2.2 环境相关寄存器
CM3用于上下文切换的寄存器主要包括通用寄存器组、状态寄存器组。
2.2.1 通用寄存器组,
如下图所示:
(1)通用寄存器
R0-R12 都是 32 位通用寄存器,用于数据操作、暂存。绝大多数 16 位 Thumb 指令只能访问 R0-R7,而 32 位 Thumb-2 指令可以访问所有寄存器。
(2)堆栈指针(SP)
R13用于栈指针(SP),指向栈区(stack),通过入栈出栈分配和释放临时变量。比如一个函数中定义了一些临时变量,调整SP指针(入栈)分配临时变量的存储空间,函数返回时,调整SP指针(出栈)释放掉堆栈空间,所以临时变量初始值是个不确定的值,因为出栈仅是调整SP指针,并不对栈空间进行归零等操作,且临时变量不宜过多,防止栈空间溢出。
CM3内核拥有两个堆栈指针:
主堆栈指针(MSP):特权级别下使用,复位后缺省使用的堆栈指针,初始值指向内存ZI-DATA栈区(Stack)的栈底(一般是高地址),代码区第一条指令就是MSP的初始化值,装置上电会先更新MSP,然后在执行reset。
进程堆栈指针(PSP):用户级别下使用,在操作系统中,指向任务的栈区,任务栈区一般从操作系统动态内存区分配,操作系统动态内存区可以根据可以根据需求设置在RW-DATA段、ZI-DATA堆区、ZI-DATA变量区。
(3)连接寄存器(LD)
R14用作连接寄存器(LD),连接寄存器当调用一个子程序时,R14存储指令返回地址,如果只有 1 级子程序调用的代码无需访问内存(堆栈内存),从而提高了子程序调用的效率。如果多于 1 级,则需要把前一级的 R14 值压到堆栈里。
程序进入handler模式(中断(异常))时,R14自动入栈保存,保存后R14更新为0xFFFFFFFX,通过指令”BX R14”进行异常返回。X的bit0为1表示返回thumb状态,bit1和bit2表示返回后sp用msp还是psp及返回到特权级别还是用户级别。合法的返回值如下所示:
0xFFFF_FFF1 | 返回handler模式【应用于中断嵌套的场景】 |
0xFFFF_FFF9 | 返回线程模式,并使用主堆栈(SP=MSP)【返回特权级线程(Thread)模式】 |
0xFFFF_FFFD | 返回线程模式,并使用线程堆栈(SP=PSP)【返回用户级线程(Thread)模式】 |
(4) 程序计数寄存器(PC)
R15用作程序计数寄存器(PC),程序计数寄存器指向NorFLASH代码区。正常运行,取指令完成,PC自动加1,既PC指向下一条指令,如果执行跳转指令或者直接修改PC寄存器的值, 就能改变程序的执行流。
2.2.2 状态寄存器组
状态字寄存器组包括:应用程序 PSR(APSR)、 中断号 PSR(IPSR)、执行 PSR(EPSR),环境保存时是三个寄存器会合并为一个32位xPSR进行保存。
2.2.3 模式切换环境自动保存
与一些高端内核不同,CM3由线程模式(Thread)进入handler模式会自动进行部分寄存器的入栈保存,下图参考自《CM3权威指南CnR2 宋岩 译》:
2.2.4 函数调用形参位置
ARM系列平台,函数形参按从左向右顺序存放在寄存器r0,r1,r2,r3里,超过4个参数值传递则放栈里。
比如函数:
void test(int iv1,int iv2,int iv3,int iv4,int iv5,int iv6);
形参:iv1、iv2、iv3、iv4分别存入寄存器r0、r1、r2、r3进行传递。
形参:iv5、iv6则入栈传递。
2.3 中断
RT_Thread操作系统调度器涉及CM3的2个中断,PendSV用于上下文切换,SysTick提供时间片,如下图:
2.3.1 PendSV(可悬起软中断)
该中断可以在高优先级中断(异常)中设置为悬起,等所有高优先级中断返回后,再执行PendSV,俗称“缓期执行”,所以PendSV的优先级一般会设置为最低。
也可以在用户级或特权级线程模式调用该服务进入handler模式。
在操作系统中,用于上下文切换,悬起 PendSV 的方法是:手工往 NVIC 的 PendSV 悬起寄存器中写 1。
2.3.2 SysTick(系统定时器),
可编程的定时中断(自检),为操作系统提供时间片,进行轮询和抢占式调度。设置方法可以参考《CM3权威指南CnR2 宋岩 译》。
三、RT_Thread线程结构
RT_Thread操作系统每个线程有独立的线程控制块(TCB)和线程栈,线程栈主要用于分配临时变量,在线程切换和初始化时,存储线程环境(通用寄存器+状态寄存器),寄存器排列顺序固定,如下图所示,可分为自动出入栈部分和手动出入栈部分,自动出入栈部分在CM3内核模式切换时由硬件自动实现(见2.2.3章节),手动出入栈部分则需要上下文切换代码实现。
四、RT_Thread启动分析
在分析操作系统上下文切换之前,先从宏观上分析操作系统启动和运行的过程。
(1)程序运行先进入特权级Thread模式,系统初始化完成后,取最高优先级线程作为第一个线程运行,执行启动第一个线程接口,触发PendSv中断,进入特权级Handler模式,寄存器LD强制为0XFFFFFFF9;执行上下文切换代码,将第一个线程中的环境(通用寄存器组+状态寄存器组)出栈,并通过执行BX 0XFFFFFFFD,跳转到用户级线程模式执行第一个线程的代码,此时使用的堆栈指针为PSP,指向第一个线程的线程栈。
(2)启动第一个线程接口同时会启动时间片中断(SysTick),为操作系统提供心跳和定时器周期轮询线程的功能,这样操作系统正常运行。
(3)由图中可以看出,操作系统正常运行后,只在用户级Thread模式和特权级Handler模式之间切换,如果不出现异常是不会在返回特权级Thread模式的。
五、RT_Thread上线文切换
5.1 上下文切换本质
RT_Thread操作系统进行上下文切换方法是挂起PendSv中断,在中断中将线程模式下的寄存器(通用寄存器+状态寄存器)入栈到当前线程栈,从另一线程(高优先级)的线程栈中出栈环境保存的寄存器数值,然后恢复到线程模式;本质是保存和切换寄器(通用寄存器+状态寄存器)数值。
5.2 上下文切换场景
RT_Thread操作系统支持抢占式调度,在线程运行过程中会涉及到上下文切换,其场景可分为2类:
被动切换:当有更高优先级线程就绪时,调度器强制将当前线程寄存器状态入栈,将高优先级线程寄存器状态出栈,实现任务切换;此过程一般在定时器线程或SysTick中断中触发。
主动切换:当前任务主动进入阻塞(vTaskDelay)、接收消息阻塞、接收信号阻塞、释放新线程等;会主动触发上下文切换。
5.3 上下文切换源码实现
上下文切换接口和相关变量如下所示:
// 相关函数接口/************【1】启动第一个线程********************************************/
void rt_hw_context_switch_to(rt_ubase_t to);
/************【2】线程中上线文切花*******************************************/
void rt_hw_context_switch(rt_ubase_t from, rt_ubase_t to);
/************【3】中断中上下文切换*******************************************/
void rt_hw_context_switch_interrupt(rt_ubase_t from, rt_ubase_t to);// 相关变量
rt_uint32_t rt_interrupt_from_thread; // 指向原线程栈顶指针
rt_uint32_t rt_interrupt_to_thread; // 指向目的线程栈栈顶指针
rt_uint32_t rt_thread_switch_interrupt_flag; // 上线文正在切换中标志
5.3.1 启动第一个线程
操作系统启动时会选择最高优先级的线程作为第一个线程启动,调用方式如下,形参为线程的栈顶指针地址,根据2.2.4章节描述,CM3内核会自动使用R0寄存器传递形参(rt_uint32_t)&to_thread->sp。
rt_hw_context_switch_to((rt_uint32_t)&to_thread->sp);
第一个线程汇编启动接口如下所示,设置好目的线程rt_interrupt_to_thread和上下文切换标志rt_thread_switch_interrupt_flag后,触发PendSv中断,在PendSv中断中执行上下文切换。
;函数声明
rt_hw_context_switch_to PROC
EXPORT rt_hw_context_switch_to;【1】目的线程rt_interrupt_to_thread指向启动线程栈指针,即(rt_uint32_t)&to_thread->spLDR r1, =rt_interrupt_to_threadSTR r0, [r1];【2】原线程rt_interrupt_from_thread指向空,设置为0LDR r1, =rt_interrupt_from_thread MOV r0, #0x0STR r0, [r1];【3】上下文切换标志rt_thread_switch_interrupt_flag 设置为1LDR r1, =rt_thread_switch_interrupt_flag MOV r0, #1STR r0, [r1];【4】设置中断PendSV和中断SysTick优先级LDR r0, =NVIC_SYSPRI2 LDR r1, =NVIC_PENDSV_PRILDR.W r2, [r0,#0x00] ; readORR r1,r1,r2 ; modifySTR r1, [r0] ; write-back;【5】挂起PendSv (上下文切换)LDR r0, =NVIC_INT_CTRL LDR r1, =NVIC_PENDSVSETSTR r1, [r0];【6】恢复 MSPLDR r0, =SCB_VTOR LDR r0, [r0]LDR r0, [r0]MSR msp, r0;【6】在处理器级别启用中断,中断启用后,由于PendSv中断已经挂起,代码会跳转到PendsV中断执行CPSIE F CPSIE I;【7】跳转PendsV中断后,系统在用户级Thread模式和特权级handler模式间切换,永远运行不到此处!ENDP
5.3.2 调度中上下文切换
操作系统调度正常运行后,时间片中断或线程可能会启动上下文切换,调用接口函数如下所示,根据2.2.4章节描述,CM3内核会自动使用R0寄存器传递形参(rt_uint32_t)&from_thread->sp,R1寄存器传递形参(rt_uint32_t)&to_thread->sp。
rt_hw_context_switch((rt_ubase_t)&from_thread->sp, (rt_ubase_t)&to_thread->sp);rt_hw_context_switch_interrupt((rt_ubase_t)&from_thread->sp, (rt_ubase_t)&to_thread->sp);
2种函数汇编代码虽然相同,但是内核模式不同,线程中执行接口是在用户级Thread模式,而时间片中断执行接口,是在特权级Handler模式。
执行接口之前均对高优先级中断进行了屏蔽,所以,以下汇编执行过程不会中断。
汇编启动接口如下所示, 设置好如下参数,
rt_uint32_t rt_interrupt_from_thread; // 原线程栈指针
rt_uint32_t rt_interrupt_to_thread; // 目的线程栈指针
rt_uint32_t rt_thread_switch_interrupt_flag; // 上线文正在切换中标志
触发PendSv中断,在PendSv中断中执行上下文切换。
rt_hw_context_switch_interruptEXPORT rt_hw_context_switch_interrupt
rt_hw_context_switch PROCEXPORT rt_hw_context_switch;【1】上下文切换标志rt_thread_switch_interrupt_flag判断LDR r2, =rt_thread_switch_interrupt_flagLDR r3, [r2];【1.1】上下文切换标志rt_thread_switch_interrupt_flag为1,跳至_reswitch CMP r3, #1BEQ _reswitch ;【1.2】上下文切换标志rt_thread_switch_interrupt_flag不为1,赋值为1MOV r3, #1STR r3, [r2] ;【2】rt_interrupt_from_thread 指向源线程栈指针LDR r2, =rt_interrupt_from_threadSTR r0, [r2]
_reswitch;【3】rt_interrupt_to_thread指向目标线程栈指针LDR r2, =rt_interrupt_to_thread STR r1, [r2];【4】触发PendSv异常(进行上下文切换)LDR r0, =NVIC_INT_CTRL LDR r1, =NVIC_PENDSVSETSTR r1, [r0];【5】跳出BX LRENDP
5.3.3 上下文切换源码分析
从5.3.1章节和5.3.2章节可见,不管是启动第一个线程还是系统运行中线程切换,最终执行的位置是PendSv中断,并且在执行中断前,已经通过全局变量传输参数。
rt_uint32_t rt_interrupt_from_thread; // 原线程栈指针
rt_uint32_t rt_interrupt_to_thread; // 目的线程栈指针
rt_uint32_t rt_thread_switch_interrupt_flag; // 上线文正在切换中标志
如果是启动第一个线程,CM3内核会从特权级Thread模式进入特权级Handler模式,R14数值更新为0XFFFFFFF9,执行完上下文切换后, R14数值更新为0XFFFFFFFD,通过指令BX 0xFFFFFFFD返回到用户级Thread模式。
如果,操作系统调度启动后,线程中进行上下文切换,会先屏蔽中断,再挂起PendSv,等恢复中断后,PendSv才能响应,CM3内核会从特权级Thread模式进入特权级Handler模式,R14数值更新为0XFFFFFFFD;寄存器R0、R1、R2、R3、R12、R14(LR)、R15(PC)、XPSR
自动通过指针PSP入栈,堆栈指针切换为MSP。
如果,时间片执行上下文切换,等时间片中断完全执行完成后,才能响应PendSv中断。
上下文切换汇编源码分析如下:
PendSV_Handler PROC
EXPORT PendSV_Handler; 【0.1】如果用户级线程模式进入中断,R0、R1、R2、R3、R12、R14(LR)、R15(PC)、XPSR; 自动通过指针PSP入栈,堆栈指针切换为MSP,; 【1】屏蔽除NMI和Fault外的中断MRS r2, PRIMASK ;中断屏蔽寄存器PRIMASK暂存,用于恢复CPSID I ;屏蔽除NMI和Fault中断; 【2】判断上下文切换标志标志rt_thread_switch_interrupt_flagLDR r0, =rt_thread_switch_interrupt_flag LDR r1, [r0] ; 【2.1】上下文切换未进行,正常切换,否则执行pendsv_exit,退出切换 CBZ r1, pendsv_exit ; 【3】清除上下文切换标志标志rt_thread_switch_interrupt_flagMOV r1, #0x00 STR r1, [r0] ; 【4】判断原线程rt_interrupt_from_thread,LDR r0, =rt_interrupt_from_thread LDR r1, [r0]; 【4.1】原线程rt_interrupt_from_thread无效,跳至switch_to_thread,直接恢复目的线程CBZ r1, switch_to_thread ; 【4.2】原线程rt_interrupt_from_thread有效,对原线程进行环境保存MRS r1, psp ;R1用作原线程栈指针STMFD r1!, {r4 - r11} ;向原线程栈入栈R4--R11,同时调整R1LDR r0, [r0] ;取原线程控制块栈指针*sp,即rt_interrupt_from_threadSTR r1, [r0] ;sp更新为最新的栈指针,即完成入栈操作的R1;【5】对目的线程进行环境恢复
switch_to_threadLDR r1, =rt_interrupt_to_threadLDR r1, [r1]LDR r1, [r1] ; R1用作目的线程栈指针LDMFD r1!, {r4 - r11} ; 从目的线程栈出栈 R4-R11,同时调整R1MSR psp, r1 ; 线程模式堆栈指针psp更换为目的线程栈指针,即完成出栈操作的R1;【6】完成手动部分,恢复屏蔽,进行自动出栈
pendsv_exitMSR PRIMASK, r2 ; 恢复屏蔽寄存器ORR lr, lr, #0x04 ; 出栈默认寄存器BX lr ; 执行BX 0xFFFFFFFD,返回用户级线程模式,根据堆栈指针psp,; 自动出栈R0、R1、R2、R3、R12、R14(LR)、R15(PC)、XPSRENDP
5.3.4 上下文切换逻辑图
5.4 上下文切换实例分析
假设线程A由运行态切换到了阻塞态,而线程B是当前最高优先级的就绪态任务,线程A主动启动PendSv中断,上下文切换过程如下所示:
由上图可见,对比切换前和切换后,线程A和线程B的栈状态正好相反,线程A进行了环境保存,线程B进行了环境恢复。
相关文章:

RT_Thread内核源码分析(一)——CM3内核和上下文切换
目录 一、程序存储分析 1.1 CM3内核寻址空间映射 1.2 程序静态存储和动态执行 二、CM3内核相关知识 2.1 操作模式和特权极别 2.2 环境相关寄存器 2.2.1 通用寄存器组, 2.2.2 状态寄存器组 2.2.3 模式切换环境自动保存 2.2.4 函数调用形参位置 2.3 …...

Android 13 高通设备热点低功耗模式
需求: Android设备开启热点,使Iphone设备连接,自动开启低数据模式 低数据模式: 低数据模式是一种在移动网络或Wi-Fi环境下,通过限制应用程序的数据使用、降低数据传输速率或禁用某些后台操作来减少数据流量消耗的优化模式。 这种模式主要用于节省数据流量费用,特别是…...

律所电子签章有效吗,怎么操作?
电子签章在很多国家和地区是合法有效的,但其有效性、使用条件和操作流程可能依据具体的法律法规而有所不同。在中国,随着《中华人民共和国电子签名法》的实施,电子签章在满足一定条件下是具有法律效力的。电子签章可以提高合同签订的效率&…...

详解 Scala 的变量、标识符、数据类型
一、注释 Scala 注释与 Java 一致 // 单行注释/** 多行注释*//*** 文档注释*/二、变量与常量 1. 语法 // 变量,类型可以省略 var varName:varClass value // 常量,类型可以省略 val valName:valClass value2. 案例 // 使用 var/val 才会在类中声明属…...
JVM-调优之-高内存占用问题排查
排查思路 1)检查jvm内存的分配情况 2)检查jvm的gc情况 3) 找出占用量比较大的对象 第一步:jmap -heap PID 查看jvm内存使用情况 jmap -heap 2525 可以看到老年代年轻代等其他内存区域内存使用率百分比 第二步:jsta…...

全球排名第一的免费开源ERP:Odoo与微信集成的应用场景解析
概述 本文介绍了世界排名第一的开源免费企业应用软件Odoo ERP和企业微信、个人微信的各种对接功能。包括微信登录的对接、微信公众号的对接、微信消息的对接、微信支付的对接、微信打卡的对接、微信小程序的对接。 微信登录的对接 Odoo的登录,除了标准的用户名/密码…...
C++中的两类智能指针std::unique_ptr与std::shared_ptr
在C中,std::unique_ptr和std::shared_ptr是两种智能指针,用于管理动态分配的内存资源,避免内存泄漏和提高代码的安全性。它们之间有一些重要的区别,下面对它们进行简要比较: std::unique_ptr: 独占所有权:…...
java中Future使用详细介绍
一、什么是Future? 在并发编程中,可以通过Future对象来异步获取结果。 使用Thread或runnable接口都不能获取异步的执行结果,因为他们没有返回值。而通过实现Callable接口和Future就可以获取异步执行的结果,当异步执行结束后&…...

docker和containerd的区别
docker和containerd的区别 1、容器运行时 1.1 容器运行时概念 容器运行时(Container Runtime)是一种负责在操作系统层面创建和管理容器的软件工具或组件。它是容器化技术的核心组件之一,用于在容器内部运行应用程序,并提供隔离…...
汇编实现流水灯
1.使能时钟: 1使能GPIO的外设时钟ldr r0,0x50000A28ldr r1,[r0]orr r1,r1,#(0x3<<4)//使能第4,5位str r1,[r0] 2.设置为输出模式 设置GPIOE10为输出模式ldr r0,0x50006000ldr r1,[r0]bic r1,r1,#(0x3<<20)orr r1,r1…...
SQL生成序列浅析
01.sqlserver版本 使用sqlserver将数据复制n条 selectt.indx,t.name,tmp.vlue from (values(1,苹果) ) as t(indx, name) ,(select[number] as vluefrom master.dbo.spt_valueswhere [type] pand [number] between 1 and 10 ) as tmpspt_values是什么 spt_values是SQL Se…...

24年gdcpc省赛C题
1279:DFS 序 先不考虑多节点,先看着颗二叉树,假设他们的父亲节点是第k个被访问的点,如果先访问左子树,那么得到的结果是a1*ka2*(k1)b1*(2k)b2*(2k1),可以发现,先访问左子树,那么右子树每次的乘以的p值实际上是左子树乘以的p值加上左子树的节点个数,比如a1*k和b1*(2k),如果不看2…...

以梦为马,不负韶华(3)-AGI在企业服务的应用
AGI在企业服务中,各应⽤已覆盖企业全流程,包含⼈⼒、法务、财税、流程⾃动化、知识管理和软件开发各领域。 由于⼤语⾔模型对⽂本处理类场景有着天然且直接的适配性,⽂本总结、⽂本内容⽣成、服务指引等发展起步早且应⽤成熟度更⾼。 在数据…...
Xshell 使用
Xshell 使用 ①xshell 安装包 ②xshell 卸载 ③xshell 同时控制多窗口 ①xshell 安装包 Xshell 7 破解版 ②xshell 卸载 第一步: 打开控制面板卸载xshell 第二步: win+R,输入regedit,打开注册表,删除xshell相关注册信息 注册表目录: 在下面两个目录中查找xshell相关…...
【yijiej】mysql报错 之 报错:Duplicate entry 字段 for key ‘表名.idx_字段’
一、问题操作 Mysql 进行insert 操作,报错:Duplicate entry 字段 for key ‘表名.idx_字段’ 原因解析:idx 是做的索引键,是具有唯一性二、问题原因(三种情况,当前我遇到的情况是第一种) 1、当 …...

解决npm卡死,无法安装依赖
npm卡死,无法安装依赖 异常描述原因分析与解决方法 异常描述 1.无法进入命令行,或是很慢没反应 2.装表格无限滚动的el-table-infinite-scroll依赖一上午了,也不能装,报错提示 原因分析与解决方法 1.命令行的问题:缓…...

速卖通测评揭秘:如何选择安全的渠道操作
许多商家对测评存在误解,认为只需进行几次测评就能迅速打造爆款。实际上,测评是一个需要计划和持久性的过程,以便让平台检测到产品的受众程度并提高产品的曝光和权重。 在进行测评时,安全是首要考虑的问题。平台可以通过设备、网…...
ping不通ip的解决方法
解决ping不通IP的问题可以通过以下几种方法: 1.检查IP配置:确保所有设备的IP地址、子网掩码和默认网关配置正确。如果使用DHCP,请确认设备已设置为自动获取IP地址,并检查DHCP服务器的地址池配置是否正确且未耗尽。 2.检查网络设…...

Linux x86_64 UEFI 启动
文章目录 前言一、UEFI二、Disk device compatibility2.1 GPT 磁盘分区表2.1.1 简介2.1.2 Linux 2.2 ESP(EFI) 文件系统2.2.1 简介2.2.2 LinuxLinux Kernel EFI Boot Stub 三、UEFI GPT grub23.1 简介3.2 引导方式 3.3 BOOTX64.EFI3.4 shimx64.efi3.5 …...
妙解设计模式之适配器模式
目录 适配器模式的概念生活中的例子在编程中的例子 软件工程中的实际应用兼容旧接口整合第三方库简化复杂接口跨平台支持 总结 适配器模式的概念 适配器模式是一种结构设计模式,它允许将接口不兼容的类通过一个适配器类进行适配,使得这些类可以一起工作…...

【JJ斗地主-注册安全分析报告】
前言 由于网站注册入口容易被黑客攻击,存在如下安全问题: 暴力破解密码,造成用户信息泄露短信盗刷的安全问题,影响业务及导致用户投诉带来经济损失,尤其是后付费客户,风险巨大,造成亏损无底洞 …...

【win | docker开启远程配置】使用 SSH 隧道访问 Docker的前操作
在主机A pycharm如何连接远程主机B win docker? 需要win docker配置什么? 快捷配置-主机B win OpenSSH SSH Server https://blog.csdn.net/z164470/article/details/121683333 winR,打开命令行,输入net start sshd,启动SSH。 或者右击我的电脑&#…...
接口不是json的内容能用Jsonpath获取吗,如果不能,我们选用什么方法处理呢?
JsonPath 是一种专门用于查询和提取 JSON 数据的查询语言(类似 XPath 用于 XML)。以下是详细解答: JsonPath 的应用场景 API 响应处理:从 REST API 返回的 JSON 数据中提取特定字段。配置文件解析:读取 J…...

buuctf——web刷题第二页
[网鼎杯 2018]Fakebook和[SWPU2019]Web1没有,共30题 目录 [BSidesCF 2020]Had a bad day [网鼎杯 2020 朱雀组]phpweb [BJDCTF2020]The mystery of ip [BUUCTF 2018]Online Tool [GXYCTF2019]禁止套娃 [GWCTF 2019]我有一个数据库 [CISCN2019 华北赛区 Day2…...
Vue3+Vite中lodash-es安装与使用指南
在 Vue 3 Vite 项目中安装和使用 lodash-es 的详细指南如下: 一、为什么选择 lodash-es? ES 模块支持:lodash-es 以原生 ES 模块格式发布,支持现代构建工具的 Tree Shaking 按需加载:只引入需要的函数,显…...

MySQL体系架构解析(二):MySQL目录与启动配置全解析
MySQL中的目录和文件 bin目录 在 MySQL 的安装目录下有一个特别重要的 bin 目录,这个目录下存放着许多可执行文件。与其他系统的可执行文件类似,这些可执行文件都是与服务器和客户端程序相关的。 启动MySQL服务器程序 在 UNIX 系统中,用…...
Apache DolphinScheduler 和 Apache Airflow 对比
Apache DolphinScheduler 和 Apache Airflow 都是开源的工作流调度平台,用于管理和编排复杂的数据处理任务和管道。以下是对两者在功能、架构、使用场景等方面的对比,用中文清晰说明: 1. 概述 Apache DolphinScheduler: 一个分布…...

PPT转图片拼贴工具 v1.0
软件介绍 这个软件的作用就是将单个PPT的每一页转换为单独的图片,然后将图片进行拼接起来。 但是我没有还没有解决一次性处理多个文件。 效果展示如下: 软件安装 软件源码 import os import re import win32com.client from PIL import Imagedef con…...
C语言的全称:(25/6/6)
C语言,全称为"C Programming Language"(C程序设计语言),是一种广泛使用的计算机编程语言。它是由Dennis Ritchie于1972年在贝尔实验室设计的,继承了B语言的许多思想,并加入了数据类型的概念及其他…...

BugKu Web渗透之网站被hei(仅仅是ctf题目名称)
启动场景,打开网页,显示如下: 目前没有看出任何异常。 步骤一: 右键查看源代码。源代码较多,也没发现异常。 步骤二: 用dirsearch扫描网站目录。 如图: 看起来shell.php很可疑。 步骤三&…...