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 操作模式和特权极别

  1. CM3有2种操作模式: 处理者模式(或中断(异常)模式 handler mode )、线程(Thread mode)模式
  2. CM3有2种权利级别: 特权级、用户级,特权级使用MSP栈指针寄存器,用户级使用PSP栈指针寄存器。两种级别的栈相互独立。
  3. 处理者模式(handler mode)只能运行在特权级别。
  4. 用户级对系统控制空间(SCS)的访问将被阻止——该空间包含了配置寄存器组以及调试组件的寄存器组。还禁止使用 MRS/MSR 访问,除了 APSR 之外的特殊功能寄存器。如果以身试法,则对于访问特殊功能寄存器的,访问操作被忽略;而对于访问 SCS 空间的,将触发fault异常。
  5. 软件触发中断寄存器可以在用户级下访问以产生软件中断(利用这个特性实现用户模式到特权模式转变)。

        各种模式之间的切换:

模式切换

触发条件

栈指针

特权级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->sp
    LDR     r1, =rt_interrupt_to_thread
    STR     r0, [r1]
   ;【2】原线程rt_interrupt_from_thread指向空,设置为0
    LDR     r1, =rt_interrupt_from_thread               
    MOV     r0, #0x0
    STR     r0, [r1]
    ;【3】上下文切换标志rt_thread_switch_interrupt_flag 设置为1
    LDR     r1, =rt_thread_switch_interrupt_flag    
    MOV     r0, #1
    STR     r0, [r1]
    ;【4】设置中断PendSV和中断SysTick优先级
    LDR     r0, =NVIC_SYSPRI2 
    LDR     r1, =NVIC_PENDSV_PRI
    LDR.W   r2, [r0,#0x00]                            ; read
    ORR     r1,r1,r2                                  ; modify
    STR     r1, [r0]                                  ; write-back

    ;【5】挂起PendSv (上下文切换)
    LDR     r0, =NVIC_INT_CTRL                          
    LDR     r1, =NVIC_PENDSVSET
    STR     r1, [r0]

    ;【6】恢复 MSP
    LDR     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_interrupt
    EXPORT rt_hw_context_switch_interrupt
rt_hw_context_switch    PROC
    EXPORT rt_hw_context_switch

    ;【1】上下文切换标志rt_thread_switch_interrupt_flag判断
    LDR     r2, =rt_thread_switch_interrupt_flag
    LDR     r3, [r2]
    ;【1.1】上下文切换标志rt_thread_switch_interrupt_flag为1,跳至_reswitch 
    CMP     r3, #1
    BEQ     _reswitch                                     
    ;【1.2】上下文切换标志rt_thread_switch_interrupt_flag不为1,赋值为1
    MOV     r3, #1
    STR     r3, [r2]                                             
    ;【2】rt_interrupt_from_thread 指向源线程栈指针
    LDR     r2, =rt_interrupt_from_thread
    STR     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_PENDSVSET
    STR     r1, [r0]
    ;【5】跳出
    BX      LR
    ENDP

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_flag
    LDR     r0, =rt_thread_switch_interrupt_flag  
    LDR     r1, [r0]            
    ; 【2.1】上下文切换未进行,正常切换,否则执行pendsv_exit,退出切换                                              
    CBZ     r1, pendsv_exit                                            
    ; 【3】清除上下文切换标志标志rt_thread_switch_interrupt_flag
    MOV     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,同时调整R1
    LDR     r0, [r0]           ;取原线程控制块栈指针*sp,即rt_interrupt_from_thread
    STR     r1, [r0]           ;sp更新为最新的栈指针,即完成入栈操作的R1
    ;【5】对目的线程进行环境恢复
switch_to_thread
    LDR     r1, =rt_interrupt_to_thread
    LDR     r1, [r1]
    LDR     r1, [r1]            ; R1用作目的线程栈指针
    LDMFD   r1!, {r4 - r11}     ; 从目的线程栈出栈 R4-R11,同时调整R1
    MSR     psp, r1             ; 线程模式堆栈指针psp更换为目的线程栈指针,即完成出栈操作的R1
    ;【6】完成手动部分,恢复屏蔽,进行自动出栈
pendsv_exit
    MSR     PRIMASK, r2         ; 恢复屏蔽寄存器
    ORR     lr, lr, #0x04       ; 出栈默认寄存器
    BX      lr                  ; 执行BX 0xFFFFFFFD,返回用户级线程模式,根据堆栈指针psp,
                                ; 自动出栈R0、R1、R2、R3、R12、R14(LR)、R15(PC)、XPSR
    ENDP

5.3.4 上下文切换逻辑图

5.4 上下文切换实例分析

        假设线程A由运行态切换到了阻塞态,而线程B是当前最高优先级的就绪态任务,线程A主动启动PendSv中断,上下文切换过程如下所示:

        由上图可见,对比切换前和切换后,线程A和线程B的栈状态正好相反,线程A进行了环境保存,线程B进行了环境恢复。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

本特利330180-51-00前置器在工业自动化中的应用与优势

本特利330180-51-00前置器在工业自动化中的应用与优势 作为PLC技术员,在工业自动化领域中,我们经常接触到各种传感器和前置器。其中,本特利330180-51-00前置器以其卓越的性能和广泛的应用领域,受到了业界的广泛关注。本文将详细介…

野外作战武器操作3D模拟实操仿真训练以便老兵能适应不同的训练需求

强国必须强军,我国在军事方面的投入持续加大,自然在军事武器培训方面不容忽视,在军事领域,3D模拟展示不仅提升了军事训练的效率,还为我们提供了更加直观、真实的武器体验。 首先,3D军事武器模拟展示能够提供…

Kyndryl 与 Nvidia 建立新的人工智能基础设施合作伙伴关系

Kyndryl与Nvidia宣布达成新的人工智能基础设施战略合作,共同推动AI技术的广泛应用。根据这一合作,Nvidia的先进AI软件解决方案将被引入Kyndryl的开放集成平台——Kyndryl Bridge,以优化基础设施工作负载,并为客户提供更高效的IT服…

青岛有哪些媒体资源?参展参会邀约报道

传媒如春雨,润物细无声,大家好,我是51媒体网胡老师。 青岛,以其优越的地理位置、丰富的产业资源、高度国际化的开放程度以及完善的会展设施,成为国内外知名的展览展会举办地。 此外,青岛作为中国重要的沿海…

leetCode.82. 删除排序链表中的重复元素 II

leetCode.82. 删除排序链表中的重复元素 II 题目思路: 代码 class Solution { public:ListNode* deleteDuplicates(ListNode* head) {auto dummy new ListNode(-1);dummy->next head;auto p dummy;while(p->next){auto q p->next->next;while(q …

开源博客项目Blog .NET Core源码学习(23:App.Hosting项目结构分析-11)

本文学习并分析App.Hosting项目中后台管理页面的标签管理页面、轮播图维护页面。 标签管理页面 标签管理页面用于显示、检索、新建、编辑、删除标签数据,以便在前台页面的首页及文章专栏等页面显示标签数据。标签管理页面附带一新建及编辑页面,以支撑新…

Python中的数据可视化:桑基图Sankey

【小白从小学Python、C、Java】 【考研初试复试毕业设计】 【Python基础AI数据分析】 Python中的数据可视化: 桑基图 Sankey [太阳]选择题 根据给定的Python代码,哪个选项是正确的? import matplotlib.pyplot as plt from matplotlib.sanke…

香橙派 AIpro的NPU随手记体验日记

昇腾AI 技术路线 8TOPS INT8(FP16)AI算力 LPDDR4X 8GB/16GB 📅 20240525 开放了原理图和源码,功能接口就不描述了手册都有描述,新手好好学习可以从底层覆盖到应用一个载板拿下 完成香橙派AIpro上手体验 镜像安装&am…

科技查新是什么?一文了解!

本文主要解答 1、什么是科技查新? 2、科技查新有哪些作用? 3、科技查新一般应用于什么地方? 4、在哪能出具正规查新报告? 5、科技查新流程是怎样的? 带着这些问题阅读这篇文章相信一定会有收获!干活内…

大模型备案VS算法备案:差异、要求与合规快照

​下图为最新的直至第五批深度合成服务算法备案信息的公告 根据目前公开的国内大模型算法备案统计来看,首批境内深度合成服务算法备案清单,总共通过了五批。 以第二批举例,境内深度合成服务算法备案清单,总共通过110家&#xff0…

Nginx企业级负载均衡:技术详解系列(13)—— 四层访问控制

你好,我是赵兴晨,97年文科程序员。 今天,咱们聊聊Nginx的一个核心功能——四层访问控制。 废话不多说,让我们直接进入正题,一探究竟! 四层访问控制 Nginx的四层访问控制指的是在传输层(TCP层…

【C++】vector常见的使用方式

前言:在上一篇中我们讲到了string类的模拟实现,今天我们将进一步的去学习vector的一些常用的使用方法。 💖 博主CSDN主页:卫卫卫的个人主页 💞 👉 专栏分类:高质量C学习 👈 💯代码仓…

简单的利用有限脉冲响应(FIR)滤波器对心电信号进行降噪(Python)

代码很简单。 import numpy as np import matplotlib.pyplot as plt#------------------------Bandstop Filter Function------------------------ def bandstop(M,low,high,Fs):#50Hz removalk1 int( (low/Fs)*M) # index 22k2 int( (high/Fs)*M) # index 27#DC removalk0 …

【头歌】计算机网络DHCP服务器配置第四关配置路由器子接口答案

头歌计算机网络DHCP服务器配置第四关配置路由器子接口操作步骤 任务描述 本关任务:配置路由器的子接口。 操作要求 在第一关的拓扑图的基础上,配置路由器及 PC 机,具体要求如下: 1、打开路由器物理接口 F0/0 ; 2、配置…

安全攻防三

一、IDS: 当黑客绕过了防火墙,你该如何发现? IDS (Intrusion Detection System,入侵检测系统) NIDS 内网中检测网络流量攻击 黑客如果已经进去内网,防火墙就没办法保护了 NIDS部署在交换机和路由器这些路…

基于Vue的自定义服务说明弹窗组件的设计与实现

基于Vue的自定义服务说明弹窗组件的设计与实现 摘要 随着技术的不断发展,前端开发面临着越来越高的复杂性和不断变化的需求。传统开发方式往往将整个系统构建为整块应用,这导致对系统的任何微小改动都可能触发整体的逻辑变更,从而增加了开发…

软件测试/测试开发丨学习笔记之Allure2测试报告

Allure2测试报告 1、使用 Allure2 运行方式-Python 1)–alluredir 参数生成测试报告。 在测试执行期间收集结果 pytest [测试用例/模块/包] --alluredir./result/ (—alluredir这个选项 用于指定存储测试结果的路径)#生成在线的测试报告 allure serve ./result2…

第一个Flutter3项目

配置flutter国内源 首先,配置flutter的国内源: env:PUB_HOSTED_URL"https://pub.flutter-io.cn"; env:FLUTTER_STORAGE_BASE_URL"https://storage.flutter-io.cn"配置gradle国内源 修改gradle\wrapper\gradle-wrapper.properties…

埃隆·马斯克的 xAI 募集 60 亿美元,瞄准 AI 超级计算机|TodayAI

埃隆马斯克(Elon Musk)创立的人工智能公司 xAI 宣布成功募集了 60 亿美元的资金,用于推动其“首批产品推向市场,建立先进的基础设施,并加速未来技术的研发”。马斯克透露,xAI 目前的估值已达到 180 亿美元&…

kafka-消费者组-点对点测试

文章目录 1、点对点测试1.1、获取 kafka-consumer-groups.sh 的帮助信息1.2、列出所有的消费者组1.3、创建消费者1并指定组 my_group11.4、创建消费者2并指定组 my_group11.5、创建消费者3并指定组 my_group11.6、创建生产者发送消息到 my_topic1 主题 1、点对点测试 1.1、获取…