当前位置: 首页 > news >正文

RT-Thread启动过程 :从汇编开始的启动流程

        这个系列参考了《嵌入式实时操作系统RT-Thread设计与实现》,会详细介绍RT-Thread的启动流程,即是如何从零开始在开发板上运行起一个RTOS内核的。本文将会以 ch32v307VCT6 开发板为例展开进行详细介绍。主要包括:startup.S、初始化与系统相关的硬件、初始化系统内核对象(例如定时器、调度器、信号)、创建main线程、初始化线程并启动调度器 这五大部分。

        在这一小节中,本文将讲述通过wsl烧录RT-Thread的基本流程。并在最后讲解startup_ch32v30x.S文件的执行过程。

一、系统运行环境

        本文在Linux系统上通过命令行的方式实现RT-Thread的下载与调试。这有几个准备步骤:

  • RT-Thread的源码获取, 在linux命令行中输入如下命令:
    git clone https://gitee.com/rtthread/rt-thread.git
  • 交叉编译工具链的下载:MRS_Toolchain_Linux_x64_V1.92.1.tar.xz 下载地址。下载好后在linux环境中解压。记录好下载路径
  • Scons工具下载: 在Linux命令行中输入以下命令:
    sudo apt install scons

        准备完毕后,切换目录到:./RT_thread/rt-thread/bsp/wch/risc-v/ch32v307v-r1 下运行(下文称其为根目录):

    scons --exec-path=../../../../../Toolchain_linux/MRS_Toolchain_Linux_x64_V1.92.1/RISC-V_Embedded_GCC/bin

        其中--exec-path=后接下载的交叉编译工具链的路径。运行成功如下所示:

        至此在当前目录下会生成rtthread.bin 和 rtthread.elf两个文件。前期的准备工作就到此结束。

二、烧录至开发板

        本文使用wsl,安装教程:WSL2 最新最全帮助小白一步步详细安装教程。

        由于wsl本身不支持usb连接,因此需要把插入电脑的usb(Windows下)接入到wsl(Linux下)中,所以还需要安装usbipd-win开源项目:usbipd-win安装。想要便捷操作的可以额外下载图形化界面wsl usb gui:wsl usb gui 下载。

        下载完成,并且usb成功接入到wsl后,为了方便操作一般都会有一个Makefile执行相关命令,其中的相对路径均从之前下载的交叉编译工具链给出:

CROSS_COMPILE := ../../../../../Toolchain_linux/MRS_Toolchain_Linux_x64_V1.92.1/RISC-V_Embedded_GCC/bin/riscv-none-embed-
OPENOCD_DIR = ../../../../../Toolchain_linux/MRS_Toolchain_Linux_x64_V1.92.1/OpenOCD/bin
OPENOCD = ${OPENOCD_DIR}/openocd
CFG = ${OPENOCD_DIR}/wch-riscv.cfg
GDB := ${CROSS_COMPILE}gdb
OBJDUMP  := ${CROSS_COMPILE}objdump
.DEFAULT_GOAL := allall:scons --exec-path=../../../../../Toolchain_linux/MRS_Toolchain_Linux_x64_V1.92.1/RISC-V_Embedded_GCC/bin.PHONY : flash
flash: @echo "------------------------"@echo "Flashing os.bin to board"@echo "------------------------"@${OPENOCD} -f ${CFG}  -c init -c halt  -c "program rtthread.bin"  -c exit.PHONY : run_openocd
run_openocd: @echo "-----------------------------------------------"@echo "Please manually kill the openocd after gbd quit"@echo "-----------------------------------------------"@${OPENOCD} -f ${CFG} &.PHONY : run_gdb
run_gdb:@${GDB} rtthread.elf -q -ex "target remote : 3333".PHONY : code
code: @${OBJDUMP} -d rtthread.elf > rtthread.asm.PHONY : clean
clean:rm -rf *.o *.bin *.elf

        把以上Makefile文件放在根目录下,并执行sudo make flash,即可成功烧录:

        使用picocom串口工具观察现象,picocom工具直接命令行输入sudo apt install picocom下载。在这里波特率为115200,默认8个数据位,1个停止位,不启用校验位。 /dev/ttyACM0为Linux系统所识别的新插入的开发板USB设备文件。按下ctrl+A   ctrl+X退出。

        当出现如下图所示的 RT 字样即说明成功启动RT-Thread。

三、startup_ch32v30x.S

        现在本文将从汇编文件开始,讲述RT-Thread是如何一步一步启动的。startup_ch32v30x.S位置在:RT_thread/rt-thread/bsp/wch/risc-v/Libraries/ch32v30x_libraries/bmsis/source

        汇编文件中的入口地址为 _start (由链接脚本ENTRY( _start )指定)。汇编文件中第13行至332行均在设置中断向量入口地址。   

        第345行在设置堆栈指针:

        第348行至357行在将.data段中的数据从flash搬移至ram:

        第360行至366行在清零 .bss 段中的数据:

        第368行至373行在写控制状态寄存器(具体作用不知):

        第377行至378行在写mstatus寄存器,mstatus寄存器结构如下图,其中比较重要的位有:

MIE:全局trap(中断和异常)开关。   

MPIE:trap前系统MIE位的状态,也可以理解为:执行mret时,MIE会被赋值位MPIE。 

MPP:trap前系统运行的模式。也可以理解为:mret时系统会运行于哪种模式。 为11则表示系统会运行在机器模式。

FS:11表示启动浮点运算

        因此这里在把MPP写为11后,执行mret就打开了全局trap(中断和异常)的开关 。

        第380行至382行在设置中断向量入口基地址,其中_vector_base在此汇编文件第29行指定。

        第384行至387行在执行开发板时钟初始化(SystemInit),并进入RT-Thread的初始化函数:

        在这里,mepc寄存器中存入了entry函数地址,在mret时,系统会跳转到mepc寄存器存放的地址继续执行,从而实现了entry函数的跳转。

SystemInit函数位置:

RT_thread/rt-thread/bsp/wch/risc-v/Libraries/ch32v30x_libraries/bmsis/source/system_ch32v30x.c:85

        首先执行SystemInit,个人认为由于系统复位,不管是哪种复位形式,寄存器都会被设置为复位值,所以SystemInit中,除了SetSysClock()函数的跳转,其他操作意义不明(我太菜了)。本文使用的ch32v307VCT6属于CH32V30x_D8C类别。

        除非是软件直接跳转到handel_reset执行复位逻辑。

void SystemInit (void)
{RCC->CTLR |= (uint32_t)0x00000001; #ifdef CH32V30x_D8CRCC->CFGR0 &= (uint32_t)0xF8FF0000; 
#elseRCC->CFGR0 &= (uint32_t)0xF0FF0000; 
#endif RCC->CTLR &= (uint32_t)0xFEF6FFFF;RCC->CTLR &= (uint32_t)0xFFFBFFFF;RCC->CFGR0 &= (uint32_t)0xFF80FFFF;#ifdef CH32V30x_D8CRCC->CTLR &= (uint32_t)0xEBFFFFFF;RCC->INTR = 0x00FF0000;RCC->CFGR2 = 0x00000000;
#elseRCC->INTR = 0x009F0000;   
#endif   SetSysClock();
}

在SetSysClock中将系统时钟设置为定义好的144MHz:

static void SetSysClock(void)
{
#ifdef SYSCLK_FREQ_HSESetSysClockToHSE();
#elif defined SYSCLK_FREQ_24MHzSetSysClockTo24();
#elif defined SYSCLK_FREQ_48MHzSetSysClockTo48();
#elif defined SYSCLK_FREQ_56MHzSetSysClockTo56();  
#elif defined SYSCLK_FREQ_72MHzSetSysClockTo72();
#elif defined SYSCLK_FREQ_96MHzSetSysClockTo96();
#elif defined SYSCLK_FREQ_120MHzSetSysClockTo120();
#elif defined SYSCLK_FREQ_144MHz  //仅144MHz有宏定义SetSysClockTo144();#endif/* If none of the define above is enabled, the HSI is used as System clock* source (default after reset) */ 
}

        在SetSysClockTo144()函数中。主要工作在 启用HSE时钟,配置PLL倍频、设置系统时钟、HCLK、PB1\PB2外设时钟

        逻辑是先启用HSE外部8MHz高速时钟,并PLL 18倍频至 144MHz 作为SYSCLOCK系统时钟。 系统时钟不分频作为HCLK的输入。 HCLK不分频作为PB2CLK输入, HCLK 2分频作为PB1CLK输入。

static void SetSysClockTo144(void)
{__IO uint32_t StartUpCounter = 0, HSEStatus = 0;RCC->CTLR |= ((uint32_t)RCC_HSEON); //启用外部高速时钟/* 等待HSE时钟准备就绪 */do{HSEStatus = RCC->CTLR & RCC_HSERDY;StartUpCounter++;} while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));/* 判断HSE是否准备就绪 */if ((RCC->CTLR & RCC_HSERDY) != RESET){HSEStatus = (uint32_t)0x01;}else{HSEStatus = (uint32_t)0x00;}/* 如果HSE准备就绪则配置PLL倍频、SYSCLK、HCLK、PB1CLK、PB2CLK */if (HSEStatus == (uint32_t)0x01){/* HCLK = SYSCLK */RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1; //设置HCLK为SYSCLK不分频输入/* PCLK2 = HCLK */RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1; //设置PB2CLK为HCLK不分频输入/* PCLK1 = HCLK/2 */RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2; //设置PB1CLK为HCLK2分频输入/*  PLL 配置: PLLCLK = HSE * 18 = 144 MHz */RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_PLLSRC | RCC_PLLMULL));#ifdef CH32V30x_D8RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSE | RCC_PLLXTPRE_HSE | RCC_PLLMULL18);
#elseRCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSE | RCC_PLLMULL18_EXTEN); //配置PLL时钟源为HSE,并设置18倍频。 实际上这里配置的PLL时钟源是prediv1,只不过prediv1系统默认输入是HSE不分频输入。
#endif/* 启用 PLL */RCC->CTLR |= RCC_PLLON;/* 等待 PLL 准备就绪 */while((RCC->CTLR & RCC_PLLRDY) == 0){}/* 设置 PLL倍频时钟 作为系统时钟 */RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_SW));RCC->CFGR0 |= (uint32_t)RCC_SW_PLL;/* Wait till PLL is used as system clock source */while ((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08){}}else{/** If HSE fails to start-up, the application will have wrong clock* configuration. User can add here some code to deal with this error*/}
}

        至此系统从汇编文件启动,到跳转到RT-Thread的初始化入口entry函数的逻辑就讲解完毕。这一部分主要是开发板启动的常规操作(数据段搬运、bss段清零、中断向量设置),以及 系统时钟的配置。接下来将介绍entry函数中是如何对RT-Thread进行初始化的。

四、RT-Thread初始化

        单核初始化过程包括:中断关闭、板级初始化、定时器初始化、调度器初始化、信号初始化、初始化创建应用main线程、初始化创建定时器线程、空闲线程初始化、僵尸线程初始化、打开中断并进行线程调度。

int rtthread_startup(void)
{
#ifdef RT_USING_SMPrt_hw_spin_lock_init(&_cpus_lock);
#endifrt_hw_local_irq_disable(); //关中断/* board level initialization* NOTE: please initialize heap inside board initialization.*/rt_hw_board_init(); //板级初始化/* show RT-Thread version */rt_show_version();/* timer system initialization */rt_system_timer_init(); //定时器初始化/* scheduler system initialization */rt_system_scheduler_init(); //调度器初始化#ifdef RT_USING_SIGNALS/* signal system initialization */rt_system_signal_init();
#endif /* RT_USING_SIGNALS *//* create init_thread */rt_application_init(); //创建用户线程/* timer thread initialization */rt_system_timer_thread_init(); //定时器线程初始化/* idle thread initialization */ rt_thread_idle_init(); //空闲线程初始化/* defunct thread initialization */rt_thread_defunct_init(); //僵尸线程初始化#ifdef RT_USING_SMPrt_hw_spin_lock(&_cpus_lock);
#endif /* RT_USING_SMP *//* start scheduler */rt_system_scheduler_start(); //线程调度,开中断并执行main函数/* never reach here */return 0;
}

        其中rt_hw_boart_init板级初始化过程需要我们根据具体的开发板进行修改。需要在其中完成系统时钟配置、为系统提供心跳、串口初始化、将系统输入输出终端绑定到这个串口。

extern uint32_t SystemCoreClock;static uint32_t _SysTick_Config(rt_uint32_t ticks)
{NVIC_SetPriority(SysTicK_IRQn, 0xf0);NVIC_SetPriority(Software_IRQn, 0xf0);NVIC_EnableIRQ(SysTicK_IRQn);NVIC_EnableIRQ(Software_IRQn);SysTick->CTLR = 0;SysTick->SR = 0;SysTick->CNT = 0;SysTick->CMP = ticks - 1;SysTick->CTLR = 0xF;return 0;
}/*** This function will initial your board.*/
void rt_hw_board_init()
{/* System Tick Configuration */_SysTick_Config(SystemCoreClock / RT_TICK_PER_SECOND);#if defined(RT_USING_USER_MAIN) && defined(RT_USING_HEAP)rt_system_heap_init((void *) HEAP_BEGIN, (void *) HEAP_END);
#endif/* USART driver initialization is open by default */
#ifdef RT_USING_SERIALrt_hw_usart_init();
#endif
#ifdef RT_USING_CONSOLErt_console_set_device(RT_CONSOLE_DEVICE_NAME);
#endif
#ifdef RT_USING_PIN/* pin must initialized before i2c */rt_hw_pin_init();
#endif/* Call components board initial (use INIT_BOARD_EXPORT()) */
#ifdef RT_USING_COMPONENTS_INITrt_components_board_init();
#endif}void SysTick_Handler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
void SysTick_Handler(void)
{GET_INT_SP();/* enter interrupt */rt_interrupt_enter();SysTick->SR = 0;rt_tick_increase();/* leave interrupt */rt_interrupt_leave();FREE_INT_SP();}

相关文章:

RT-Thread启动过程 :从汇编开始的启动流程

这个系列参考了《嵌入式实时操作系统RT-Thread设计与实现》,会详细介绍RT-Thread的启动流程,即是如何从零开始在开发板上运行起一个RTOS内核的。本文将会以 ch32v307VCT6 开发板为例展开进行详细介绍。主要包括:startup.S、初始化与系统相关的…...

Scala—“==“和“equals“用法(附与Java对比)

Scala 字符串比较—""和"equals"用法 Scala 的 在 Scala 中, 是一个方法调用,实际上等价于调用 equals 方法。不仅适用于字符串,还可以用于任何类型,并且自动处理 null。 Demo: Java 的 在 J…...

$route和$router的区别

在 Vue.js 中,$route 和 $router 都是 Vue Router 提供的对象,但它们有不同的用途和功能。 1. $router $router 是 Vue Router 实例的引用,它允许你通过 JavaScript 进行路由的控制和导航。你可以通过 $router 来执行路由的操作&#xff0c…...

[工具升级问题] 钉钉(linux版)升级带来的小麻烦

本文由Markdown语法编辑器编辑完成。 1. 背景: 今日钉钉又发布了新的升级版本。由于我工作时使用的是Ubuntu 20.04版本,收到的升级推送信息是,可以升级到最新的7.6.25-Release版本。根据钉钉官方给出的历次更新版说明,这个新的版本&#xf…...

Leetcode经典题13--接雨水

题目描述 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。 输入输出示例 输入:height [0,1,0,2,1,0,1,3,2,1,2,1] 输出:6 解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1…...

yarn修改缓存位置

查看缓存位置 以下三个命令分别为:bin是yarn存储命令的二进制文件,global存储全局node_modules ,cache存储用下下载缓存,查看本机目前的目录: 查看bin目录命令:yarn global bin 查看global目录命令&…...

OpenHarmony-3.HDF input子系统(5)

HDF input 子系统OpenHarmony-4.0-Release 1.Input 概述 输入设备是用户与计算机系统进行人机交互的主要装置之一,是用户与计算机或者其他设备通信的桥梁。常见的输入设备有键盘、鼠标、游戏杆、触摸屏等。本文档将介绍基于 HDF_Input 模型的触摸屏器件 IC 为 GT91…...

RabbitMQ 消息持久化/镜像队列/lazy对时延影响

测试背景: 不同条件下RabbitMQ不同队列类型的生产时延测试: 测试环境: 机型:rabbimtq.2u4g.cluster 背景流量:1000 TPS 测试条件: 消息大小 4k,消息条数为1000条,时延取值为平均…...

【深度学习】深刻理解Swin Transformer

Swin Transformer 是一种基于 Transformer 的视觉模型,由 Microsoft 研究团队提出,旨在解决传统 Transformer 模型在计算机视觉任务中的高计算复杂度问题。其全称是 Shifted Window Transformer,通过引入分层架构和滑动窗口机制,S…...

[2015~2024]SmartMediaKit音视频直播技术演进之路

技术背景 2015年,因应急指挥项目需求,我们实现了RTMP推送音视频采集推送(采集摄像头和麦克风数据)模块,在我们做好了RTMP推送模块后,苦于没有一个满足我们毫秒级延迟诉求的RTMP播放器,于是第一…...

redis 使用Lettuce 当redis挂掉重启之后 网络是怎么重新连接

Lettuce是一个高性能的Java Redis客户端,支持同步、异步和反应式编程模式 Lettuce的核心功能包括: ‌高性能‌:通过使用Netty作为底层网络通信框架,实现了非阻塞IO,提高了性能。‌丰富的API‌:提供了丰富…...

【IntelliJ IDEA 集成工具】TalkX - AI编程助手

前言 在数字化时代,技术的迅猛发展给软件开发者带来了更多的挑战和机遇。为了提高技术开发群体在繁多项目中的编码效率和质量,他们需要一个强大而专业的工具来辅助开发过程,而正是为了满足这一需求,TalkX 应运而生。 一、概述 1…...

二叉搜索树Ⅲ【东北大学oj数据结构8-3】C++

二叉搜索树 III B:在二叉搜索树II中加入delete指令,创建程序对二叉搜索树T执行如下指令。 插入 k:将key k 插入到 T 中。 find k:报告T中是否存在key k。 delete k:删除key为 k 的节点。 打印:使用中序树遍…...

【面试笔记】CPU 缓存机制

CPU 缓存机制 1. CPU Cache 与 MMU1.1 MMU 是什么?TLB 又是什么?他们是怎么工作的?2.2 简述 Cache 与 MMU 的协作关系?2.3 简述 Cache 与 MMU 的协作工作流程? 2. CPU 多层次缓存2.1 什么是 CPU 的多层次缓存结构&…...

MySQL基础函数使用

目录 简介 1. 单行函数 1.1 字符串函数 1.2 日期函数 1.3 数值函数 1.4 转换函数 1.5 其他函数 2. 多行函数 示例: 3. 数据分组 示例: 4. DQL单表关键字执行顺序 示例: 5. 多表查询 示例: 6. 表与表的外连接 示例…...

解决docker环境下aspose-words转换word成pdf后乱码问题

描述 环境&#xff1a;docker 部署工具&#xff1a;Jenkins 需求&#xff1a;本地上传的word文档需要转换成pdf 问题&#xff1a;转换之后的pdf文档出现小框框&#xff08;乱码&#xff09; 转换成PDF的操作 pom&#xff1a; <dependency><groupId>org.apach…...

C# 生成随机数的方法

C# 提供了一种强大而方便的工具类 Random &#xff0c;用于生成随机数。这里将分类讨论如何通过 C# 实现随机数生成&#xff0c;以及应用于实际情况中的一些具体方案。 一、Random 类概述 Random 类表示一个伪随机数生成器&#xff0c;用于生成满足随机性统计要求的数字序列。…...

ip_done

文章目录 路由结论 IP分片 数据链路层重谈Mac地址MAC帧报头局域网的通信原理MSS&#xff0c;以及MAC帧对上层的影响ARP协议 1.公司是不是这样呢? 类似的要给运营商交钱&#xff0c;构建公司的子网&#xff0c;具有公司级别的入口路由器 2&#xff0e;为什么要这样呢?? IP地…...

3D可视化引擎HOOPS Visualize与HOOPS Luminate Bridge的功能与应用

HOOPS Visualize HPS / HOOPS Luminate Bridge为开发者提供了强大的工具&#xff0c;用于在CAD应用中集成逼真的渲染能力。本文旨在梳理该桥接产品的核心功能、使用方法及应用场景&#xff0c;为用户快速上手并充分利用产品特性提供指导。 桥接产品的核心功能概述 HOOPS Lumi…...

Docder 搭建Redis分片集群 散片插槽 数据分片 故障转移 Java连接

介绍 使多个 Redis 实例共同工作&#xff0c;实现数据的水平扩展。通过将数据分片到多个节点上&#xff0c;Redis 集群能够在不牺牲性能的前提下扩展存储容量和处理能力&#xff0c;从而支持更高并发的请求。Redis 集群不仅支持数据分片&#xff0c;还提供了自动故障转移和高可…...

java_网络服务相关_gateway_nacos_feign区别联系

1. spring-cloud-starter-gateway 作用&#xff1a;作为微服务架构的网关&#xff0c;统一入口&#xff0c;处理所有外部请求。 核心能力&#xff1a; 路由转发&#xff08;基于路径、服务名等&#xff09;过滤器&#xff08;鉴权、限流、日志、Header 处理&#xff09;支持负…...

Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动

一、前言说明 在2011版本的gb28181协议中&#xff0c;拉取视频流只要求udp方式&#xff0c;从2016开始要求新增支持tcp被动和tcp主动两种方式&#xff0c;udp理论上会丢包的&#xff0c;所以实际使用过程可能会出现画面花屏的情况&#xff0c;而tcp肯定不丢包&#xff0c;起码…...

2025年能源电力系统与流体力学国际会议 (EPSFD 2025)

2025年能源电力系统与流体力学国际会议&#xff08;EPSFD 2025&#xff09;将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会&#xff0c;EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...

FFmpeg 低延迟同屏方案

引言 在实时互动需求激增的当下&#xff0c;无论是在线教育中的师生同屏演示、远程办公的屏幕共享协作&#xff0c;还是游戏直播的画面实时传输&#xff0c;低延迟同屏已成为保障用户体验的核心指标。FFmpeg 作为一款功能强大的多媒体框架&#xff0c;凭借其灵活的编解码、数据…...

【位运算】消失的两个数字(hard)

消失的两个数字&#xff08;hard&#xff09; 题⽬描述&#xff1a;解法&#xff08;位运算&#xff09;&#xff1a;Java 算法代码&#xff1a;更简便代码 题⽬链接&#xff1a;⾯试题 17.19. 消失的两个数字 题⽬描述&#xff1a; 给定⼀个数组&#xff0c;包含从 1 到 N 所有…...

从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)

设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile&#xff0c;新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...

现代密码学 | 椭圆曲线密码学—附py代码

Elliptic Curve Cryptography 椭圆曲线密码学&#xff08;ECC&#xff09;是一种基于有限域上椭圆曲线数学特性的公钥加密技术。其核心原理涉及椭圆曲线的代数性质、离散对数问题以及有限域上的运算。 椭圆曲线密码学是多种数字签名算法的基础&#xff0c;例如椭圆曲线数字签…...

鱼香ros docker配置镜像报错:https://registry-1.docker.io/v2/

使用鱼香ros一件安装docker时的https://registry-1.docker.io/v2/问题 一键安装指令 wget http://fishros.com/install -O fishros && . fishros出现问题&#xff1a;docker pull 失败 网络不同&#xff0c;需要使用镜像源 按照如下步骤操作 sudo vi /etc/docker/dae…...

大学生职业发展与就业创业指导教学评价

这里是引用 作为软工2203/2204班的学生&#xff0c;我们非常感谢您在《大学生职业发展与就业创业指导》课程中的悉心教导。这门课程对我们即将面临实习和就业的工科学生来说至关重要&#xff0c;而您认真负责的教学态度&#xff0c;让课程的每一部分都充满了实用价值。 尤其让我…...

MySQL账号权限管理指南:安全创建账户与精细授权技巧

在MySQL数据库管理中&#xff0c;合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号&#xff1f; 最小权限原则&#xf…...