从零手写操作系统之RVOS环境搭建-01

news2024/7/4 5:59:26

从零手写操作系统之RVOS环境搭建-01

  • 背景介绍
    • 操作系统的定义
    • 操作系统的分类
    • 典型的 RTOS 介绍
    • 课程系统RVOS简介
  • Hello World
    • QEMU介绍
      • QEMU-virt 地址映射
    • 系统引导
      • 引导程序要做哪些事情
        • 如何判断当前hart是不是第一个hart?
        • 如何初始化栈?
    • 如何在屏幕输出Hello World
      • 通过串口输出
      • UART特点
      • UART的物理接口
      • UART的通信协议
      • NS16550a 编程接口介绍
      • NS16550a 的初始化
      • NS16550a 的数据读写


本系列参考: 学习开发一个RISC-V上的操作系统 - 汪辰 - 2021春 整理而来,主要作为xv6操作系统学习的一个前置基础。

RVOS是本课程基于RISC-V搭建的简易操作系统名称。

课程代码和环境搭建教程参考github仓库: https://github.com/plctlab/riscv-operating-system-mooc/blob/main/howto-run-with-ubuntu1804_zh.md


背景介绍

操作系统的定义

在这里插入图片描述


操作系统的分类

在这里插入图片描述


典型的 RTOS 介绍

在这里插入图片描述

在这里插入图片描述


课程系统RVOS简介

在这里插入图片描述


Hello World

QEMU介绍

在这里插入图片描述
片上系统(System-on-Chip,SoC)是指将多个组件和功能集成到单个集成电路(IC)上的完整电子系统或微芯片。它将处理器核心、存储器、输入/输出接口、外设和其他系统组件等多个硬件组件集成在一颗芯片上。

  • SoC旨在为特定应用提供全面的解决方案,例如移动设备、嵌入式系统或物联网(IoT)设备。通过在单个芯片上集成多个组件,SoC具有功耗降低、尺寸更小、性能提升和成本效益等优势。
  • SoC广泛应用于各种电子设备,包括智能手机、平板电脑、智能电视、游戏机、汽车系统和许多其他消费电子产品和工业应用。它们在实现现代电子设备的功能和性能方面发挥着至关重要的作用。

在这里插入图片描述
QEMU(Quick EMUlator)是一个开源的虚拟化软件,用于模拟多种硬件平台和体系结构。“QEMU virt” 是 QEMU 中的一个虚拟平台,用于模拟基于 ARM 架构的虚拟机。它是 QEMU 支持的多个虚拟平台之一,常用于开发和测试 ARM 架构相关的软件和系统。

  • QEMU virt 平台支持运行多种操作系统,包括 Linux、Android 等。它提供了一组虚拟设备和功能,包括 CPU、内存、磁盘、网络等,可以模拟一个完整的虚拟环境,使开发人员能够在此环境中进行应用程序的调试、性能优化、驱动程序的开发等。
  • 通过使用 QEMU virt 平台,开发人员可以在不需要实际硬件的情况下进行 ARM 架构相关软件的开发和测试工作,极大地提高了开发效率和灵活性。同时,QEMU 还提供了丰富的命令行和配置选项,使用户可以根据自己的需求对虚拟机进行定制和扩展。

QEMU-virt 地址映射

在这里插入图片描述
QEMU 是一个虚拟化平台,它通过模拟不同的硬件设备和处理器架构,提供了统一的编址和访问方式。在 QEMU 中,所有的设备都被虚拟化为统一的地址空间,并通过内存映射来访问这些设备。这样,操作系统和应用程序可以使用统一的编程接口和地址空间访问不同的设备,而不需要关注实际的物理硬件细节。


系统引导

在这里插入图片描述
QEMU跑起来之后,BootLoader会跳转到0X8000-0000处继续执行。

QEMU的运行命令参数会携带-kernel参数,该参数指明加载我们的os.elf内核文件到内存。并且os.elf文件在链接时也指明了text代码段被加载到内存中的0x8000 0000位置处。


引导程序要做哪些事情

QEMU默认提供8个模拟的hart,这8个hart一上电都会去运行我们的kernel程序,课程为了简单起见,默认只会使用一个hart,其余hart让其进行空转:
在这里插入图片描述
因此,我们需要在kernel启动程序中编写程序完成上面的需求。


如何判断当前hart是不是第一个hart?

  • 除了x0 - x31 这32个默认提供的通用寄存器外,RSIC-V对于每个特权模式都有自己的一条寄存器,也就是控制状态寄存器CSR
    在这里插入图片描述
  • RSIC-V定义了特殊的指令来访问这些CSR寄存器,因为机器一上电默认运行在Machine模式下,所以我们先关注如何读写Machine模式下的CSR寄存器
    在这里插入图片描述

我们关注的是最上面的Machine Information Registers这组寄存器,这组寄存器中存放了当前机器的相关状态信息,比如: 当前hart的id.

为了读写这组状态寄存器,我们需要使用专门的CSR指令:
在这里插入图片描述
CSRRW指令(原子读写CSR寄存器): 一般可用于实现两个寄存器值的交换,并且这个过程是原子性的,不可打断

如果RD位为x0,则相当于将rs赋值给csr寄存器,因为向x0寄存器写入数据是没有意义的。

在这里插入图片描述
CSRRS(原子读并设置CSR中某一位的值):

如果RS位为x0,则只是单独对CSR寄存器进行读取。

在这里插入图片描述


经过上面的分析可知,如果要实现我们的需求,则需要读取mhartid寄存器:
在这里插入图片描述
核心汇编代码如下:

_start:
	# park harts with id != 0
	csrr	t0, mhartid		# read current hart id
	mv	tp, t0			# keep CPU's hartid in its tp for later usage.
	bnez	t0, park		# if we're not on the halt0,we park the hart
   ...
park:
	wfi
	j	park

Wait for Interrupt instruction (WFI) 是 RISC-V架构定义的一条休眠指令。当处理器执行到 WFI 指令之后,将会停止执行当前的指令流,进入一种空闲状态。这种空闲状态可以被称为“休眠;"状态,直到处理器接收到中断,


如何初始化栈?

这里给出start.S汇编代码:

#include "platform.h"
	 # 每个硬件线程的栈大小为1024字节
	.equ	STACK_SIZE, 1024
    # 声明符号 _start 为全局符号。它是程序的入口点。 
	.global	_start
    # 指定以下代码属于 .text 段,其中包含可执行指令。它标志着 _start 代码的开始。
	.text
_start:
	# park harts with id != 0
    csrr t0, mhartid     # 读取当前硬件线程的ID
    mv tp, t0            # 将CPU的硬件线程ID保存在tp寄存器中以备后用
    bnez t0, park        # 如果不是硬件线程0,则进入休眠状态

   slli t0, t0, 10      # 将硬件线程ID左移10位(相当于乘以1024)
   la sp, stacks + STACK_SIZE    # 将初始栈指针设置为第一个栈空间的末尾
   add sp, sp, t0       # 将当前硬件线程的栈指针移动到栈空间中的相应位置

	j	start_kernel		# hart 0 jump to c

park:
	wfi
	j	park

stacks:
	.skip	STACK_SIZE * MAXNUM_CPU  # 为所有硬件线程分配栈空间
	.end				# 文件结束

这里我们主要关注,如何做到为每个hart分配独立的栈空间(彼此隔离):

	# Setup stacks, the stack grows from bottom to top, so we put the
	# stack pointer to the very end of the stack range.
	slli	t0, t0, 10		# 将当前硬件线程 ID 左移 10 位(相当于乘以 1024)
	la	sp, stacks + STACK_SIZE	# 将栈指针设置为第一个栈空间的末尾
	add	sp, sp, t0		# 将当前硬件线程的栈指针移动到其在栈空间中的位置

这段代码通过使用每个硬件线程的唯一硬件线程ID来隔离每个hart的栈空间。每个硬件线程的ID不同,因此通过将硬件线程ID左移10位(相当于乘以1024),可以为每个硬件线程分配独立的栈空间。

  • 首先,通过读取当前硬件线程的ID并将其存储在寄存器t0中,使用csrr指令。
  • 然后,使用la指令将栈指针sp设置为stacks标签加上STACK_SIZE,即第一个栈空间的末尾地址。这样可以将栈指针设置为栈空间的最末尾。
  • 最后,使用add指令将栈指针sp与硬件线程ID左移10位的结果相加,以将当前硬件线程的栈指针移动到其在栈空间中的位置。由于每个硬件线程的ID不同,因此每个硬件线程的栈指针会被正确地定位到其分配的独立栈空间的位置。
  • 这样,每个硬件线程就具有了自己独立的栈空间,实现了栈空间的隔离。

例如:

  • 当有多个 hart(核心)运行时,每个 hart 都需要有自己独立的栈空间,以避免互相干扰和冲突。

  • 假设有两个 harts,hart0 和 hart1,每个 hart 的栈空间大小为 1024 字节(STACK_SIZE)。

  • 首先,我们将每个 hart 的 ID 左移 10 位(slli t0, t0, 10),相当于将 hart0 的 ID 乘以 1024。然后,我们将 stacks 的起始地址(栈空间的起始地址)与这个偏移量相加,即 la sp, stacks + STACK_SIZE。

  • 假设 stacks 的起始地址为 0x1000。对于 hart0,它的 ID 是 0,因此 t0 的值为 0,偏移量为 0。所以 la sp, stacks + STACK_SIZE 将 sp 设置为 0x1000 + 1024 = 0x1400, add sp, sp, t0 —> 0x1400 + 0,即 hart0 的栈空间的起始地址。

  • 对于 hart1,它的 ID 是 1,因此 t0 的值为 1,偏移量为 1024。所以 la sp, stacks + STACK_SIZE 将 sp 设置为 0x1000 + 1024 = 0x1400, add sp, sp, t0 --> 0x1400 + 1024 = 0x1800,即 hart1 的栈空间的起始地址。

  • 通过这样的操作,我们将不同的 harts 的栈空间隔离开来,每个 hart 都有自己独立的栈空间,互不干扰。当各个 harts 运行时,它们可以在各自的栈空间上进行栈操作,而不会相互冲突。
    在这里插入图片描述


如何在屏幕输出Hello World

通过串口输出

在这里插入图片描述

UART代表通用异步收发器(Universal Asynchronous Receiver-Transmitter)。它是一种常用的串行通信协议,用于两个设备之间的通信。UART协议允许一次只传输和接收一位数据,通过单个数据线进行通信。

UART被广泛应用于各种应用中,包括嵌入式系统、微控制器以及计算机、调制解调器和传感器等不同设备之间的通信接口。它提供了一种简单高效的方法,用于设备之间的数据传输和接收。

UART通信包括起始位,随后是数据位(通常为8位),用于错误检测的可选奇偶校验位,以及停止位或多个停止位。起始位表示数据帧的开始,而停止位表示帧的结束。数据以异步方式传输,意味着设备之间没有共享时钟信号。

UART在点对点配置中运行,其中两个设备直接连接使用两条数据线:一条用于发送数据(TX),一条用于接收数据(RX)。一个设备的TX线连接到另一个设备的RX线,反之亦然。这允许设备之间的双向通信。


UART特点

在这里插入图片描述


UART的物理接口

在这里插入图片描述
UART物理接口通常由以下几个引脚组成:

  1. TX (Transmit): 该引脚用于发送数据。数据从UART发送器输出到这个引脚,经过串行传输发送到接收设备。

  2. RX (Receive): 该引脚用于接收数据。接收设备通过该引脚接收从发送设备发送的数据。

  3. GND (Ground): 地线引脚,用于提供电路的地连接。

除了上述必要的引脚外,UART接口可能还包括其他可选引脚,如:

  1. RTS (Request to Send): 请求发送引脚,用于控制数据发送。发送设备通过该引脚向接收设备发出发送请求。

  2. CTS (Clear to Send): 清除发送引脚,用于确认接收设备准备好接收数据。接收设备通过该引脚向发送设备发送准备好接收的信号。

  3. DTR (Data Terminal Ready): 数据终端就绪引脚,用于指示设备准备好进行数据通信。

  4. DSR (Data Set Ready): 数据集就绪引脚,用于指示接收设备准备好接收数据。

  5. RI (Ring Indicator): 响铃指示引脚,用于指示电话线路上是否有电话呼叫信号。

这些引脚的具体命名和功能可能在不同的设备和应用中有所不同,但上述列举的是UART接口常见的引脚。


UART的通信协议

在这里插入图片描述


NS16550a 编程接口介绍

QUME是一个知名的串口模拟器和仿真工具,它可以模拟各种串口设备,包括NS16550A芯片所提供的功能。因此,通过QUME,可以模拟NS16550A串口芯片的行为和接口。

使用QUME,可以创建虚拟串口设备,并通过配置参数来模拟NS16550A芯片的寄存器、数据传输、中断和状态等功能。因此我们能够进行串口通信的仿真和测试,而无需实际的硬件设备。

QUME提供了丰富的功能,包括模拟不同的串口参数、配置波特率、数据位数、校验位、停止位等,并且可以模拟接收和发送数据,监测串口状态和中断等。这样可以在虚拟环境中进行串口编程和调试,以确保代码在实际环境中正常工作。

需要注意的是,QUME是一个软件工具,它提供了对串口功能的模拟和仿真,但并不直接与硬件设备通信。因此,在实际使用中,QUME可以作为开发、测试和调试串口通信应用程序的有用工具,但在实际的硬件系统中,需要使用NS16550A芯片或其他串口硬件来实现真正的串口通信。
在这里插入图片描述
NS16550A是一种常用的串口通信芯片,它提供了一个编程接口,用于配置和控制串口通信功能。以下是NS16550A芯片的编程接口的基本介绍:

  1. 数据寄存器 (Data Register):用于读取和写入串口数据。通过读取数据寄存器,可以获取接收到的数据;通过写入数据寄存器,可以发送数据。

  2. 状态寄存器 (Status Register):用于获取串口的状态信息。可以通过读取状态寄存器来了解串口的接收和发送状态,包括是否有接收到数据、是否可以发送数据等。

  3. 控制寄存器 (Control Register):用于配置和控制串口的各种参数和功能。通过写入控制寄存器,可以设置波特率、数据位数、校验位、停止位等串口参数,以及启用或禁用接收和发送功能。

  4. 波特率发生器 (Baud Rate Generator):用于设置串口的波特率。通过设置波特率发生器的值,可以定义串口通信的传输速率。

  5. 中断控制寄存器 (Interrupt Control Register):用于配置和控制串口的中断功能。通过写入中断控制寄存器,可以启用或禁用不同类型的中断,如接收中断、发送中断等。

在这里插入图片描述

通过访问这些寄存器,可以对NS16550A芯片进行编程控制,实现对串口通信的配置、数据传输和状态监测等操作。具体的编程接口使用方式和寄存器地址等信息可以参考NS16550A芯片的数据手册或相关文档。


NS16550a 的初始化

  • 关闭中断
  • 设置波特率
  • 设置异步数据通信格式
    在这里插入图片描述

在这里,"关闭中断"指的是禁用串口(UART)的中断功能,即禁止串口触发和处理中断事件。

串口通信中的中断通常用于以下目的:

  • 接收中断:当串口接收到数据时,会触发接收中断,通知处理器有新的数据可供处理。
  • 发送中断:当串口发送缓冲区为空时,会触发发送中断,通知处理器可以继续发送新的数据。

通过禁用中断,就是告诉串口不要触发和处理这些中断事件。这样可以避免在初始化期间由于中断的发生而引起的干扰和错误。

禁用中断不会影响串口的数据传输功能,它仅仅是关闭了中断的触发和处理机制。一旦初始化完成,并且需要启用中断来处理接收和发送数据的中断事件时,可以通过适当的设置和配置重新启用中断。

完整代码注释如下: (可参考NS16550a相关文档进行学习)

void uart_init()
{
	/* disable interrupts. */
	uart_write_reg(IER, 0x00);

	/*
	 * Setting baud rate. Just a demo here if we care about the divisor,
	 * but for our purpose [QEMU-virt], this doesn't really do anything.
	 *
	 * Notice that the divisor register DLL (divisor latch least) and DLM (divisor
	 * latch most) have the same base address as the receiver/transmitter and the
	 * interrupt enable register. To change what the base address points to, we
	 * open the "divisor latch" by writing 1 into the Divisor Latch Access Bit
	 * (DLAB), which is bit index 7 of the Line Control Register (LCR).
	 *
	 * Regarding the baud rate value, see [1] "BAUD RATE GENERATOR PROGRAMMING TABLE".
	 * We use 38.4K when 1.8432 MHZ crystal, so the corresponding value is 3.
	 * And due to the divisor register is two bytes (16 bits), so we need to
	 * split the value of 3(0x0003) into two bytes, DLL stores the low byte,
	 * DLM stores the high byte.
	 */
	uint8_t lcr = uart_read_reg(LCR);
	uart_write_reg(LCR, lcr | (1 << 7));
	uart_write_reg(DLL, 0x03);
	uart_write_reg(DLM, 0x00);

	/*
	 * Continue setting the asynchronous data communication format.
	 * - number of the word length: 8 bits
	 * - number of stop bits: 1 bit when word length is 8 bits
	 * - no parity
	 * - no break control
	 * - disabled baud latch
	 */
	lcr = 0;
	uart_write_reg(LCR, lcr | (3 << 0));
}

相关宏定义:

//读写uart寄存器的相关宏定义
#define uart_read_reg(reg) (*(UART_REG(reg)))
#define uart_write_reg(reg, v) (*(UART_REG(reg)) = (v))
/*
 * The UART control registers are memory-mapped at address UART0. 
 * This macro returns the address of one of the registers.
 */
#define UART_REG(reg) ((volatile uint8_t *)(UART0 + reg))
/* This machine puts UART registers here in physical memory. */
#define UART0 0x10000000L

NS16550a 的数据读写

在这里插入图片描述

  • TSR寄存器(Transmit Status Register)是一个用于发送状态信息的寄存器,通常在串口通信芯片中存在。
  • LSR寄存器(Line Status Register)是一个用于线路状态信息的寄存器,通常在串口通信芯片中存在。

这里采用轮询的方式实现数据的发送—>putc:

int uart_putc(char ch){
    //不断读取LSR寄存器,获取其第五位,判断是否为0,为0表示空闲
	while ((uart_read_reg(LSR) & LSR_TX_IDLE) == 0);
	//如果空闲就向THR寄存器中写入数据
	return uart_write_reg(THR, ch);
}

启动函数:

extern void uart_init(void);
extern void uart_puts(char *s);

void start_kernel(void){
    //初始化串口设备
	uart_init();
	//输出字符
	uart_puts("Hello, RVOS!\n");

	while (1) {}; // stop here!
}

编译运行:
在这里插入图片描述

注意如何退出qemu:

在这里插入图片描述

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

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

相关文章

基于java SpringBoot框架和Vue的智能停车场管理系统

近年来&#xff0c;中国不仅综合国力大幅提升&#xff0c;国民经济也快速增长&#xff0c;推动了中国汽车工业的发展。技术的飞速发展逐渐降低了汽车的制造成本&#xff0c;越来越受欢迎。今天&#xff0c;大多数家庭都有能力购买汽车&#xff0c;因此&#xff0c;中国城市的汽…

R语言手动绘制连续线条的校准曲线(Calibration curve)(4)

校准曲线图表示的是预测值和实际值的差距&#xff0c;作为预测模型的重要部分&#xff0c;目前很多函数能绘制校准曲线。 一般分为两种&#xff0c;一种是通过Hosmer-Lemeshow检验&#xff0c;把P值分为10等分&#xff0c;求出每等分的预测值和实际值的差距。 我们既往已经通…

基于组件化开发思想的微信小程序开发框架

跨端框架的出现为小程序应用的开发带来了巨大的便利性和灵活性。它们提供了统一的开发方式、代码复用的能力&#xff0c;并且与小程序容器技术紧密结合&#xff0c;实现了一次编码、多端运行的目标。开发者可以根据项目需求和团队技术栈选择合适的跨端框架&#xff0c;从而在不…

【大数据工具】Spark 伪分布式、分布式集群搭建

Spark 集群搭建 Spark 安装包下载地址&#xff1a;https://archive.apache.org/dist/spark/ 1. Spark 伪分布式安装 安装前提&#xff1a;安装 Spark 前需要先安装好 JDK 1. 上传并解压 Spark 安装包 使用 fileZilla 或其他文件传输工具上传 Spark 安装包&#xff1a;spar…

简单易懂的 nvm 和 Node.js 版本控制指南

NVM是Node.js的版本管理工具&#xff0c;可以方便地在不同版本的Node.js之间切换。它可以通过命令行或者脚本来管理Node.js的版本&#xff0c;支持在同一台机器上安装多个版本的Node.js&#xff0c;并能够方便地切换它们。 NVM的主要功能包括&#xff1a; 安装和卸载Node.js的不…

2022年国赛高教杯数学建模A题波浪能最大输出功率设计解题全过程文档及程序

2022年国赛高教杯数学建模 A题 波浪能最大输出功率设计 原题再现 随着经济和社会的发展&#xff0c;人类面临能源需求和环境污染的双重挑战&#xff0c;发展可再生能源产业已成为世界各国的共识。波浪能作为一种重要的海洋可再生能源&#xff0c;分布广泛&#xff0c;储量丰富…

DevExpress WinForms v23.1新功能抢先看——支持系统强调色更改

DevExpress WinForm 下一个主要版本&#xff08;v23.1&#xff09;将在6月份左右发布&#xff0c;本文将为大家介绍在早期访问预览版&#xff08;EAP&#xff09;中包含的新功能。 PS&#xff1a;DevExpress WinForm拥有180组件和UI库&#xff0c;能为Windows Forms平台创建具…

5月琐碎但值得的事情

转眼间时间就来到了6月份&#xff0c;又该写5月的思考总结了&#xff0c;依然记录一些5月份发生的小事或者收获&#xff0c; 这些内容本意给我记录生活的&#xff0c;如果对你有一些帮助就更好了。 往期&#xff1a; 1月的碎碎念&#xff0c;但是很有必要 二月的一些琐事&#…

chatgpt赋能python:Python如何阻止弹窗

Python如何阻止弹窗 Python是一种高级编程语言&#xff0c;它具有广泛的应用和丰富的库。它还可以被用于开发自动化程序&#xff0c;包括阻止弹窗。在本文中&#xff0c;我们将介绍如何使用Python阻止弹出窗口&#xff0c;并探讨防止弹窗的原因。 为什么要防止弹窗&#xff1…

Librosa库——语音识别,语音音色识别训练及应用

很多同学以为语音识别是非常难的&#xff0c;其实并不然&#xff0c;起初我也是这么认为&#xff0c;但后来发现语音识别是最简单的&#xff0c;因为同学们可能不知道Python有一个音频处理库Librosa&#xff0c;这个库非常的强大&#xff0c;可以进行音频处理、频谱表示、幅度转…

精彩回顾 | 来看 QTF 量化科技嘉年华上的 DolphinDB

6月2日至6月3日&#xff0c;2023“量变质变”量化科技嘉年华在上海世博中心圆满举办。 DolphinDB 作为联合主办方&#xff0c;在6月3日上午的“因子挖掘与机器学习”分论坛中&#xff0c;为广大量化粉丝们奉上了一场干货满满的主题分享与圆桌讨论&#xff0c;现场座无虚席&…

直击CACLP:新冠红利退潮,谁在裸泳,谁在冲刺?

5月可谓是很多医疗人马不停蹄的一个月&#xff0c;上海的第87届CMEF刚结束&#xff0c;28至30日&#xff0c;体外诊断&#xff08;IVD&#xff09;旗帜性行业盛会——第20届CACLP也在南昌绿地国际博览中心顺利落幕了。 纷享销客已经连续五年参与这两大行业盛会了&#xff0c;…

助力工业物联网,工业大数据之其他维度:组织机构【十五】

文章目录 01&#xff1a;其他维度&#xff1a;组织机构02&#xff1a;其他维度&#xff1a;仓库、物流附录一&#xff1a;常见问题1.错误&#xff1a;没有开启Cross Join2.错误&#xff1a;Unable to move source 01&#xff1a;其他维度&#xff1a;组织机构 目标&#xff1a;…

ChatGPT使用进阶,你一定要知道的应用技巧

鉴于ChatGPT的巨大能力&#xff0c;深入学习ChatGPT使用技巧势在必行。作为伴随着ChatGPT等大语言模型&#xff08;LLM&#xff09;出现的还有一个新的工程领域&#xff1a;提示工程&#xff08;Prompt Engineering&#xff09;。 提示工程&#xff08;Prompt Engineering&…

前端053_单点登录SSO_刷新令牌获取新令牌

刷新令牌获取新令牌 1、创建刷新令牌组件2、添加刷新组件路由配置3、EasyMock 添加刷新令牌接口4、定义 Api 调用刷新令牌接口5、Vuex 发送请求与重置状态6、重构刷新令牌组件7、测试当应用系统请求后台资源接口时,要在请求头带上 accessToken 去请求接口,如果 accessToken 有…

【Python】Python系列教程-- Python3 OS 文件/目录方法(二十七)

文章目录 前言语法错误异常异常处理try/excepttry/except...elsetry-finally 语句 抛出异常用户自定义异常定义清理行为预定义的清理行为 前言 往期回顾&#xff1a; Python系列教程–Python3介绍&#xff08;一&#xff09;Python系列教程–Python3 环境搭建&#xff08;二&…

前端数据传输失败

1 问题 通过postman可用传输数据到java但页面数据传输不成功 postman结果&#xff1a; 页面传输结果&#xff1a; 2 方法 在使用页面传输数据时不能直接使用send(username,password)&#xff0c;我们需要使用FromData属性&#xff0c;将username和password添加到FromData里&…

小程序框架Mpx的下一代脚手架升级之路|滴滴开源

导读 Mpx开源之路已经走过五个年头&#xff0c;目前支持了滴滴内部全量的小程序业务开发&#xff0c;是滴滴开源委员会孵化的精品项目。 2022年至今&#xff0c;我们对 Mpx 框架进行了多项重要功能升级&#xff0c;包括组合式API开发规范、分包异步构建支持、单元测试能力建设和…

C++知识第四篇之多态

目录 一.认识多态1. 多态分类2. 虚函数a. 介绍b. 虚函数的重写c. 协变d. 析构函数 3. 多态构成条件a. 虚函数调用多态b. 析构函数多态 4. C11新特性a. overrideb. final 5. 重载、重写(覆盖)、重定义(隐藏) 二. 抽象类1.介绍2. 接口继承 三. 多态原理1. 虚函数表2. 打印虚函数表…

力扣高频SQL50题(基础版)——第八天

力扣高频SQL50题(基础版)——第八天 1 游戏玩法分析 IV 1.1 题目内容 1.1.1 基本题目信息 1.1.2 示例输入输出 1.2 示例sql语句 SELECT ROUND(count(a2.f_date)/(SELECT count(distinct player_id) FROM Activity),2) fraction FROM Activity a1 INNER JOIN (SELECT player…