xv6启动过程
entry,S -> start.c -> main.c -> proc.c中的 userinit 函数 -> initcode.S -> init.c
entry.S
// entry.S# qemu -kernel loads the kernel at 0x80000000# and causes each CPU to jump there.# kernel.ld causes the following code to# be placed at 0x80000000.
.section .text
.global _entry
_entry:# set up a stack for C.# stack0 is declared in start.c,# with a 4096-byte stack per CPU.# sp = stack0 + (hartid * 4096)la sp, stack0li a0, 1024*4csrr a1, mhartidaddi a1, a1, 1mul a0, a0, a1add sp, sp, a0# jump to start() in start.ccall start
spin:j spin
entry.S 用于在 QEMU 模拟器上启动 xv6 操作系统。它定义了启动过程中的入口点和初始化堆栈的过程。以下是对这段代码的逐行解释:
-
.section .text
:指定以下代码应该放置在文本段(.text
)中,这是用于存储程序代码的标准段。 -
.global _entry
:声明_entry
是一个全局符号,这意味着它可以被外部引用,例如链接器或其他汇编文件。 -
_entry:
:定义了一个标签_entry
,这是当前代码段的入口点。la sp, stack0
:使用la
指令加载stack0
的地址到sp
寄存器。stack0
是在start.c
中声明的一个数组,为每个 CPU 核心提供了一个 4096 字节大小的堆栈。li a0, 1024*4
:将立即数1024*4
(即 4096)加载到a0
寄存器。这个值是每个 CPU 核心堆栈的大小。csrr a1, mhartid
:使用csrr
指令读取mhartid
寄存器的值到a1
寄存器。mhartid
寄存器包含当前硬件线程(hart)的 ID。addi a1, a1, 1
:将a1
寄存器的值加 1。这是因为mhartid
寄存器的值从 0 开始,而stack0
数组的索引从 1 开始。mul a0, a0, a1
:将a0
寄存器(堆栈大小)与a1
寄存器(hart ID 索引)相乘,计算出当前 hart 的堆栈在stack0
数组中的偏移量。add sp, sp, a0
:将计算出的偏移量加到sp
寄存器(当前 hart 的堆栈顶部)上,设置正确的堆栈指针。call start
:跳转到start
函数。start
函数是start.c
中定义的,它初始化操作系统并进入调度器的主循环。
-
spin:
:定义了一个标签spin
。j spin
:无限跳转到spin
标签,形成 spinlock(自旋锁)。如果start
函数返回,控制权会回到这里,但由于start
函数不应该返回,这个自旋锁实际上会阻止 CPU 执行任何其他代码。
这段代码确保每个 CPU 核心都有一个独立的堆栈(sp = stack0 + (hartid + 1) * 4KB
),并且它们都跳转到 start
函数开始执行。这是操作系统启动过程的一部分,它在物理硬件或模拟器上启动操作系统之前设置必要的环境。
为什么必须首先设置栈指针?
- 时间顺序上的必要性:
_entry
是内核的第一条指令,紧接着需要调用start
函数。如果栈指针未设置,call
指令会尝试将返回地址压栈,但由于sp
无效,可能会写入非法地址,导致崩溃。 - 硬件约束:RISC-V 的启动规范不保证
sp
的初始值,QEMU 也不会自动初始化栈指针。因此,操作系统必须显式设置。 - 后续依赖:
start.c
中的代码会进一步调用其他函数(如 timerinit),这些函数依赖栈来存储局部变量和调用上下文。如果没有栈,整个初始化过程无法继续。
start.c
start 函数负责初始化 CPU 的特权模式、内存管理和中断处理,并最终跳转到用户空间的 main 函数执行。
- 设置 M 模式的返回特权级别:
通过读取和修改mstatus
寄存器,将 Previous Privilege (PP) 模式设置为 Supervisor (S) 模式。当后续执行 mret 时,CPU 将从 M 模式切换到 S 模式。 - 设置 M Exception Program Counter:
将mepc
寄存器设置为main
函数的地址。这意味着当 CPU 从中断或异常返回时,它将跳转到main
函数继续执行。 - 禁用分页机制:
通过将satp
寄存器设置为 0,暂时禁用分页机制。此时内核还未初始化页表(这在 main() 中的 kvminit() 完成),因此暂时禁用分页。这是在操作系统完全启动并设置好内存管理单元 (MMU) 之前的必要步骤。 - 设置中断和异常代理到 S 模式:
通过写入medeleg
和mideleg
寄存器,将所有中断和异常代理给 Supervisor 模式处理。同时,通过设置sie
寄存器启用 Supervisor 模式的中断和异常。 - 配置物理内存保护:
通过设置pmpaddr
和pmpcfg
寄存器,配置物理内存保护,允许 Supervisor 模式访问所有物理内存地址。 - 初始化时钟中断:
调用timerinit
函数,初始化时钟中断,使 CPU 能够在需要时请求中断。 - 保存硬件线程 ID:
将当前硬件线程 (hart) 的 ID 保存到tp
寄存器中,以便后续可以通过cpuid
函数获取。 - 切换到 Supervisor 模式并跳转到 main 函数:
通过执行mret
指令,切换到 Supervisor 模式,并跳转到main
函数开始执行。这标志着操作系统启动流程的完成,控制权转移到用户空间的入口点。
这段代码是操作系统启动过程中的关键部分,它确保了 CPU 正确配置并准备好执行用户空间的程序。通过这些初始化步骤,操作系统能够管理内存、处理中断和异常,并提供一个安全的环境来运行用户程序。
// entry.S jumps here in machine mode on stack0.
void
start()
{// set M Previous Privilege mode to Supervisor, for mret.// 这些行设置了 RISC-V 机器状态寄存器(mstatus)的 Previous Privilege (PP) 模式为 Supervisor(S)模式。// 这是为了确保在执行 mret 指令(从中断或异常返回)时,CPU 能够返回到 Supervisor 模式。unsigned long x = r_mstatus();x &= ~MSTATUS_MPP_MASK;x |= MSTATUS_MPP_S;w_mstatus(x);// set M Exception Program Counter to main, for mret.// requires gcc -mcmodel=medany// 这里将RISC-V机器异常程序计数器(mepc)设置为main()函数的地址。// 这是中断或异常处理完成后,CPU应该返回执行的地址。w_mepc((uint64)main);// disable paging for now.// 这行代码禁用了分页机制,将 RISC-V 机器的页表根地址寄存器(satp)设置为 0。w_satp(0);// delegate all interrupts and exceptions to supervisor mode.// 这些行设置了中断和异常的代理(delegation),允许Supervisor模式处理所有的中断和异常。w_medeleg(0xffff);w_mideleg(0xffff);w_sie(r_sie() | SIE_SEIE | SIE_STIE | SIE_SSIE);// configure Physical Memory Protection to give supervisor mode// access to all of physical memory.// 这里配置了物理内存保护(PMP),允许Supervisor模式访问所有物理内存。w_pmpaddr0(0x3fffffffffffffull);w_pmpcfg0(0xf);// ask for clock interrupts.// 这行代码初始化了时钟中断,以便 CPU 可以在需要时请求中断。timerinit();// keep each CPU's hartid in its tp register, for cpuid().// 这里将当前硬件线程(hart)的ID保存到RISC-V的线程指针(tp)寄存器中,// 以便后续可以通过cpuid()函数获取int id = r_mhartid();w_tp(id);// switch to supervisor mode and jump to main().// 这行代码通过执行 mret 指令(从中断或异常返回)来切换到 Supervisor 模式,并跳转到 main() 函数开始执行。asm volatile("mret");
}
main.c
void
main()
{// 核0执行的代码if(cpuid() == 0){consoleinit();printfinit();printf("\n");printf("xv6 kernel is booting\n");printf("\n");kinit(); // physical page allocator 初始化物理页分配器,这是操作系统管理内存的关键部分。kvminit(); // create kernel page table 初始化内核页表,这是虚拟内存管理的基础。kvminithart(); // turn on paging 在当前 hart 上启用分页机制。procinit(); // process table 初始化进程表,这是操作系统管理进程的关键数据结构。trapinit(); // trap vectors 初始化中断和异常处理向量,这是操作系统响应硬件事件的关键部分。trapinithart(); // install kernel trap vector 在当前 hart 上安装内核陷阱向量。plicinit(); // set up interrupt controller 初始化 PLIC(可编程中断控制器),这是处理设备中断的关键组件。plicinithart(); // ask PLIC for device interrupts在当前 hart 上启用 PLIC。binit(); // buffer cache 初始化缓冲区缓存,这是文件系统的一部分,用于提高磁盘 I/O 性能。iinit(); // inode table 初始化 inode 表,这是文件系统管理文件和目录的关键数据结构。fileinit(); // file table 初始化文件表,这也是文件系统的一部分。virtio_disk_init(); // emulated hard disk 初始化虚拟 I/O 磁盘,这是模拟的硬盘设备,用于提供文件系统存储。userinit(); // first user process 初始化用户进程,这是创建第一个用户级进程的步骤。__sync_synchronize();started = 1; // 确保所有初始化操作都完成后,设置 started 标志为 1,表示系统已经启动。} else { // 其他核执行while(started == 0);__sync_synchronize();printf("hart %d starting\n", cpuid());kvminithart(); // turn on pagingtrapinithart(); // install kernel trap vectorplicinithart(); // ask PLIC for device interrupts}scheduler(); // 最后,调用 scheduler() 函数启动操作系统的调度器,这是操作系统多任务处理的核心部分。一旦调度器启动,它将负责在多个进程之间进行上下文切换,确保系统高效运行。
}
main() 的初始化顺序遵循“从底层到高层”、“从基础到应用”的原则。每个步骤都为后续步骤铺路,避免未定义行为。
初始化顺序的逻辑
- 基础设施优先:
- 控制台和打印(consoleinit、printfinit)→ 调试支持。
- 内存管理(kinit、kvminit、kvminithart)→ 分配和寻址基础。
- 核心功能:
- 进程(procinit)和陷阱(trapinit、trapinithart)→ 运行和管理任务。
- 中断(plicinit、plicinithart)→ 处理外部事件。
- 文件系统和设备:
- 缓冲区(binit)、inode(iinit)、文件表(fileinit)、磁盘(virtio_disk_init)→ 支持用户态文件操作。
- 用户态准备:
- userinit() → 启动用户进程。
必须先初始化什么?
- kinit() 必须在任何需要内存分配的步骤之前(如 kvminit、procinit)。
- kvminit() 和 kvminithart() 必须在访问虚拟地址的步骤之前(如陷阱处理、设备驱动)。
- trapinit() 和 trapinithart() 必须在可能触发中断的步骤之前(如 plicinit、virtio_disk_init)。
- 文件系统组件(binit、iinit、fileinit)必须按顺序初始化,因为它们有层次依赖。
- userinit() 必须最后,因为它依赖所有前置组件。
userinit
// Set up first user process.
void
userinit(void)
{struct proc *p;p = allocproc();initproc = p;// allocate one user page and copy init's instructions// and data into it.// 初始化新进程的页表,并为它分配一页用户内存。// initcode 是一个包含启动代码的数组,它的大小是一页(PGSIZE)。这页内存将包含init进程的代码和数据。uvminit(p->pagetable, initcode, sizeof(initcode));p->sz = PGSIZE;// prepare for the very first "return" from kernel to user.// 设置新进程的初始用户程序计数器(EPC)和用户栈指针(SP)。EPC 设置为 0,这通常是用户程序入口点的地址。// SP 设置为一页的大小,这是用户栈的起始地址。p->trapframe->epc = 0; // user program counterp->trapframe->sp = PGSIZE; // user stack pointer// 使用 safestrcpy 函数复制字符串 "initcode" 到新进程的名字字段。这个字段用于调试和识别进程。safestrcpy(p->name, "initcode", sizeof(p->name));// 获取根目录的 inode 并将其赋给新进程的当前工作目录(cwd)p->cwd = namei("/");p->state = RUNNABLE;release(&p->lock);
}
在 allocproc()
中会通过 p->context.ra = (uint64)forkret;
设置 ra
寄存器,ra
寄存器会存储新创建的进程进行第一个 switch
调用会返回的位置,即 forkret
函数。
// A fork child's very first scheduling by scheduler()
// will swtch to forkret.
void
forkret(void)
{static int first = 1;// Still holding p->lock from scheduler.release(&myproc()->lock);if (first) {// File system initialization must be run in the context of a// regular process (e.g., because it calls sleep), and thus cannot// be run from main().first = 0;fsinit(ROOTDEV);}// 这是重点!!!从内核空间 -> 用户空间usertrapret();
}
执行完 userinit
后,main
函数运行 scheduler
进行第一次调度,调度现在仅有的 RUNNABLE 状态的进程 init
。
initcode.S
# Initial process that execs /init.
# This code runs in user space.#include "syscall.h"# exec(init, argv)
.globl start
start:la a0, initla a1, argvli a7, SYS_exececall# for(;;) exit();
exit:li a7, SYS_exitecalljal exit# char init[] = "/init\0";
init:.string "/init\0"# char *argv[] = { init, 0 };
.p2align 2
argv:.long init.long 0
start
部分调用 SYS_exec
系统调用执行 init
函数。
// init.c
// init: The initial user-level program#include "kernel/types.h"
#include "kernel/stat.h"
#include "kernel/spinlock.h"
#include "kernel/sleeplock.h"
#include "kernel/fs.h"
#include "kernel/file.h"
#include "user/user.h"
#include "kernel/fcntl.h"char *argv[] = { "sh", 0 };int
main(void)
{int pid, wpid;if(open("console", O_RDWR) < 0){mknod("console", CONSOLE, 0);open("console", O_RDWR);}dup(0); // stdoutdup(0); // stderrfor(;;){printf("init: starting sh\n");pid = fork();if(pid < 0){printf("init: fork failed\n");exit(1);}if(pid == 0){exec("sh", argv);printf("init: exec sh failed\n");exit(1);}for(;;){// this call to wait() returns if the shell exits,// or if a parentless process exits.wpid = wait((int *) 0);if(wpid == pid){// the shell exited; restart it.break;} else if(wpid < 0){printf("init: wait returned an error\n");exit(1);} else {// it was a parentless process; do nothing.}}}
}
init
进程是系统启动后的第一个进程,它的主要任务是启动 shell(通常是一个命令行解释器),并且如果 shell 进程退出,则不断地重启它。这样可以确保系统始终有一个可用的 shell 来接受用户命令。
通过这种机制,init
进程确保了系统中始终有一个可用的 shell,即使用户退出了当前的 shell 会话。这种设计使得 xv6 操作系统具有更好的健壮性和可用性。
相关文章:
xv6启动过程
entry,S -> start.c -> main.c -> proc.c中的 userinit 函数 -> initcode.S -> init.c entry.S // entry.S# qemu -kernel loads the kernel at 0x80000000# and causes each CPU to jump there.# kernel.ld causes the following code to# be placed at 0x800…...
【秣厉科技】LabVIEW工具包——OpenCV 教程(18):highgui 模块
文章目录 前言highgui 模块总结 前言 需要下载安装OpenCV工具包的朋友,请前往 此处 ;系统要求:Windows系统,LabVIEW>2018,兼容32位和64位。 highgui 模块 尽量别用,要不我删了吧? LabVIEW…...
基于OPENCV的图像透视矫正
这段代码的主要功能是对输入的图像进行透视矫正。它会读取一张图像,检测图像中最大的四边形轮廓,然后对该四边形区域进行透视变换,将其矫正为正视图,最后保存矫正后的图像。 模块导入说明 python import cv2 import numpy as n…...
数据结构----------顺序查找,折半查找和分块查找(java实现)
import java.util.Arrays;//顺序查找法 public class Main {public static void main(String[] args) {//查找表int[] arr {4, 3, 5, 1, 2};System.out.print("5在数组中的索引:");System.out.println(SearchSeq(arr, 5));Arrays.sort(arr);System.out.print("…...
整数编码 - 华为OD统一考试(A卷、JavaScript)
题目描述 实现一种整数编码方法,使得待编码的数字越小,编码后所占用的字节数越小。 编码规则如下: 编码时7位一组,每个字节的低7位用于存储待编码数字的补码。字节的最高位表示后续是否还有字节,置1表示后面还有更多的字节&…...
CompletableFuture:整合、超时、完成事件与批量处理
引言 在异步编程实践中,我们不仅需要处理单个任务的执行流程,更需要应对多个异步任务之间的复杂交互。本文将通过实际案例解析以下核心功能: 双任务整合:合并两个独立任务的结果高效超时控制:防止异步操作无限等待完…...
【LeetCode 热题100】45:跳跃游戏 II(详细解析)(Go语言版)
🚀 力扣 45:跳跃游戏 II(全解法详解) 📌 题目描述 给你一个非负整数数组 nums,表示你最初位于数组的第一个位置。 数组中的每个元素表示你在该位置可以跳跃的最大长度。 你的目标是使用 最少的跳跃次数 到…...
【C/C++】滑动谜题(leetcode T773)
核心考点:广度优先搜索 (BFS)、哈希表、字符串、状态转移 题目描述: 在一个 2 x 3 的板上(board)有 5 块砖瓦,用数字 1~5 来表示, 以及一块空缺用 0 来表示。一次 移动 定义为选择 0 与一个相邻的数字(上…...
python用x08覆盖上一次输出来模拟控制台等待效果,pycharm运行sys.stdout.write在控制台无打印的解决方法
一个多进程程序,主进程阻塞,子进程不断打印等待效果直到主进程结束,原理是\x08在ascii中表示退格键,理解为打印完后立马删掉打印下一个内容。 import sys, time import multiprocessing DELAY 0.1 DISPLAY [ |, /, -, \\ …...
【嵌入式开发】使用Linux系统调用编程练习
一、进程和线程的概念及基础用法 在Linux系统中,进程(Process)和线程(Thread)是操作系统进行任务调度的基本单位,它们既有联系又有区别。 1.1 进程和线程介绍 1.1.1 进程(Process)…...
React框架的Concurrent Mode
以下是关于 Concurrent Mode 的系统梳理: 一、Concurrent Mode 的诞生背景 传统渲染的局限性 同步阻塞:React 15 的 Stack Reconciler 无法中断渲染流程优先级缺失:用户交互与后台任务同等对待资源竞争:网络请求与渲染任务无法智能调度核心设计目标 可中断渲染:允许高优先…...
ER-图,详情和画法
一、E-R图的核心元素 1.实体 表示现实中对象或概念,用矩形表示 示例:用户、老师、学生 2.属性 描述实体的特征,用椭圆表示。 分为主键(用户id) 和非主键(用户昵称) 3.关系 表示实体间的…...
深度学习图像分类数据集—十种西红柿病态叶识别分类
该数据集为图像分类数据集,适用于ResNet、VGG等卷积神经网络,SENet、CBAM等注意力机制相关算法,Vision Transformer等Transformer相关算法。 数据集信息介绍:10种西红柿病态叶识别分类:Bacterial_spot,Earl…...
【Flask开发】嘿马文学web完整flask项目第3篇:2.用户认证,2.用户认证【附代码文档】
教程总体简介:2. 目标 1.1产品与开发 1.2环境配置 1.3 运行方式 1.4目录说明 1.5数据库设计 2.用户认证 Json Web Token(JWT) 3.书架 4.1分类列表 5.搜索 5.3搜索-精准&高匹配&推荐 6.小说 6.4推荐-同类热门推荐 7.浏览记录 8.1配置-阅读偏好 8.配置 9.1项目…...
基于Pyhon的京东笔记本电脑数据可视化分析系统
【Python】基于Pyhon的京东笔记本电脑数据可视化分析系统 (完整系统源码开发笔记详细部署教程)✅ 目录 一、项目简介二、项目界面展示三、项目视频展示 一、项目简介 本项目基于Python语言开发,通过Flask框架与Bootstrap的结合,实…...
『不废话』之Llama 4实测小报
2025年4月5日Llama 4一开源,随后OpenRouter等平台就提供免费调用。对于中文社区来,官方的测评结果其实意义不大(原因先按下不表),就看知乎、微博、B站、twitter上的真实感受,最重要的是自己的真实案例测评。…...
llama.cpp 和 vLLM 的详细对比分析
llama.cpp 和 vLLM 的详细对比分析,基于最新技术动态(2025年4月)整理: 1. 核心定位 维度llama.cppvLLM设计目标轻量化边缘计算,突破硬件限制(如手机/树莓派)企业级高性能推理,优化G…...
Windows 操作系统使用vscode 配置GTK4
本篇教程,主要介绍在vscode中如何配置使用GTK4,并运行一个简易的入门案例。 一、程序代码 1、demo.cpp #include <gtk/gtk.h>// 定义一个回调函数,用于处理按钮点击事件 static void on_button_clicked(GtkButton *button...
swift-汇编分析多态原理、init
一、结构体和类的本质区别 结构体 编译完就知道调用谁 类 (类似c 的虚函数表) 12 直接将对象将来要调用的函数内存地址提前放进类型信息里面,这些类型信息编译完就确定你将来要调用谁,运行过程过程中就去那块内存里面找 方法的存…...
Docker基础2
如需转载,标记出处 本次我们将下载一个 Docker 镜像,从镜像中启动容器 上一章,安装 Docker 时,获得两个主要组件: Docker 客户端 Docker 守护进程(有时称为“服务器”或“引擎”) 守护进程实…...
labelme json 标签转yolo txt【记录】
01 labelme json 转 txt(w_convert_labelme_to_yolo.py) #WT 将labelme json标签格式转换为YOLO txt格式 # 导入所需模块 import cv2 # OpenCV用于图像处理 import os # 操作系统路径管理 import json # JSON文件解析 import glob # 文件通配符搜索…...
Java 集合框架与 Stream 流深入剖析(重点详细讲解)
目录 引言 一、ArrayList 1. 概述 2. 特点 动态扩容 初始容量 扩容倍数 随机访问高效 插入和删除效率低 3. 代码示例 4. 分析 二、HashSet 1. 概述 2. 特点 唯一性 插入、删除和查找效率高 无序性 3. 代码示例 4. 分析 三、HashMap 1. 概述 2. 特点 键唯…...
实操(多线程特点、健壮性降低、缺乏访问控制)Linux
线程 创建两个线程 makefile(添加原生线程库) mythread:thread.ccg -o $ $^ -stdc11 -lpthread .PHONY:clean clean:rm -f mythreadthread.cc #include <iostream> #include <pthread.h> #include<unistd.h>using namespace std;…...
微信小程序学习实录12:掌握大数据量轨迹展示的MySQL结构设计
获取经纬度信息后,mysql建立数据表po_trajectory,字段包含tra_id、longitude、latitude、tra_time和openid。 为微信小程序创建的 po_trajectory 数据表,字段包含 tra_id、longitude、latitude、tra_time 和 openid,从结构设计上…...
语法: result=ldexp (value, exp);
LDEXP( ) 语法: resultldexp (value, exp); 参数: value是一个浮点数; exp是一个有符号的整型数; 返回值: result同value保持一致,是一个浮点数,结果是value乘以2的exp次方. 功能: ldexp( ) 该函数是用一个浮点数乘以2的多少(整数)次方. 有效性: 适合所有的CPU设备…...
STM32学习之硬件FPU(原理篇)
📢:如果你也对机器人、人工智能感兴趣,看来我们志同道合✨ 📢:不妨浏览一下我的博客主页【https://blog.csdn.net/weixin_51244852】 📢:文章若有幸对你有帮助,可点赞 👍…...
QEMU源码全解析 —— 块设备虚拟化(15)
接前一篇文章:QEMU源码全解析 —— 块设备虚拟化(14) 本文内容参考: 《趣谈Linux操作系统》 —— 刘超,极客时间 《QEMU/KVM源码解析与应用》 —— 李强,机械工业出版社 特此致谢! QEMU初始化阶段的块设备虚拟化 本回解析virtio_blk_device_realize函数中的virtio_a…...
Web3(阶段一:入门)——哈希算法
一、简述 哈希算法(Hash Algorithm)是一种将任意长度的输入数据转换为固定长度输出(哈希值)的数学函数。其核心作用是通过不可逆的计算生成唯一标识数据的 “数字指纹”,广泛应用于数据完整性验证、密码学、区块链等领…...
高频面试题(含笔试高频算法整理)基本总结回顾63
干货分享,感谢您的阅读! (暂存篇---后续会删除,完整版和持续更新见高频面试题基本总结回顾(含笔试高频算法整理)) 备注:引用请标注出处,同时存在的问题请在相关博客留言…...
如何深入理解C#中的备忘录模式(Memento Pattern)设计模式
在软件开发中,设计模式是一种解决特定问题的通用方法,而备忘录模式(Memento Pattern)是其中一种用于保存对象状态的结构型设计模式。它允许你在不暴露对象内部结构的情况下,保存和恢复对象的状态。本文将深入探讨C#中的…...
存储引擎 / 事务 / 索引
1. 存储引擎 MySQL 中特有的术语。 (Oracle 有,但不叫这个名字) 是一种表存储 / 组织数据的方式 不同的存储引擎,表存储数据的方式不同 1.1 查看存储引擎 命令: show engines \g(或大写:G…...
药店管理系统
https://download.csdn.net/download/weixin_57836618/90572873 软件架构 Java SpringBoot Mybatis/Mybatis-plus Mysql 项目功能说明 促销管理:零售出库、零售退货采购管理:采购订单、采购入库、采购退货销售管理:销售订单、物流信息、…...
Kafka 的发展历程
Kafka 作为一个高性能的分布式消息流平台,从诞生到现在已经有了长足的发展,经历了多个版本的迭代。下面是 Kafka 的 发展历史、版本迭代 以及 新特性 的概述。 1. Kafka 的诞生与早期发展 2010年:Kafka 由 LinkedIn 的工程师 Jay Kreps、Ne…...
PowerBI 之DAX 3:文本、信息、财务、时间智能函数
文章目录 一、文本函数1.1 FORMAT函数1.1.1 数字格式1.1.2 日期/时间格式1.1.3 自定义格式 1.2 CONCATENATE与CONCATENATEX1.2.1 返回多个类别名称1.2.2 返回多个类别的名称和数据,并排序 1.3 使用SEARCH进行模糊查找 二、信息函数2.1 ISINSCOPE 三、财务函数3.1 PV…...
GESP C++三级 知识点讲解
C编程三级标准 (一)知识点详述 (1)了解二进制数据编码:原码、反码、补码。 (2)掌握数据的进制转换:二进制、八进制、十进制、十六进制。 (3)掌握位运算:与(&)、或(|)、非(~)、异或(^)、左移(<<)、右移(>>)的基本使用方法及原理。 (4)了解算法的概念与描述&…...
如何访问和使用Sora:OpenAI视频生成模型的完整指南
OpenAI的Sora作为革命性的视频生成模型,能够根据文本提示创建长达60秒的高质量视频内容。本教程将详细介绍目前Sora的使用方法和访问途径。 一、Sora当前访问状态(2024年3月更新) 目前Sora仍处于有限访问阶段,OpenAI采取了分阶段…...
MyBatis 分页插件使用教程
MyBatis 分页插件使用教程 MyBatis 是一款优秀的持久层框架,但原生的 MyBatis 并不支持分页查询。为了简化分页操作,MyBatis 官方和第三方提供了多种分页插件,最常用的就是 MyBatis-Plus 的分页插件。本文详细介绍 MyBatis-Plus 分页插件的使…...
OpenDriveVLA:通过大型视觉-语言-动作模型实现端到端自动驾驶
25年3月来自慕尼黑工大和慕尼黑大学的论文“OpenDriveVLA: Towards End-to-end Autonomous Driving with Large Vision Language Action Model”。 OpenDriveVLA,一种专为端到端自动驾驶而设计的视觉-语言-动作 (VLA) 模型。OpenDriveVLA 以开源预训练大型视觉-语言…...
深入探究C++ 运算符重载:以日期类为例
目录 前言 一、运算符重载基础 1.1 运算符重载原理 1.2 示例代码 二、赋值运算符重载 2.1 赋值运算符重载格式 2.2 代码实现 2.3 注意事项 三、前置和后置重载 3.1 前置重载 3.2 后置重载 四、日期类的完整实现 4.1 获取某月天数 4.2 完整类定义 五、总结 前言 …...
2024第十五届蓝桥杯大赛软件赛省赛Java大学B组 报数游戏 类斐波那契循环数 分布式队列 食堂 最优分组 星际旅行 LITS游戏 拼十字
目录 A 报数游戏 B 类斐波那契循环数 C 分布式队列 D 食堂 E 最优分组 F 星际旅行 G LITS 游戏 H 拼十字 今天心血来潮把去年的题目又做了一遍... 本人去年大一 拿的是全省第五进的国赛 而如今的已经是一名 codeforces 1500 分的入门级别的算竞选手了 下周又是蓝桥杯…...
4月6日随笔
一觉起来十点多 其实六点和九点分别醒过一次。 起来之后点了个侍卫草推荐的猪排饭,真的巨好吃,猪排很脆,溏心蛋也很香 但是因为酒店十二点半要退房,就匆匆吃完了猪排和一半米饭就走了 今天下午在科技楼写了一会作业,…...
[GN] sigrokdecode 模块学习指南 --- 准备阶段
系列文章目录 文章目录 系列文章目录前言指南libsigrokdecode 学习一、构建环境安装libsigrokdecode安装 sigrok-cli(命令行工具)安装 PulseView(图形界面)关联 libsigrokdecode完整验证参数解释 二、BUG解决1. 确保编译时启用了 …...
【力扣hot100题】(056)电话号码的字母组合
依旧是很经典的回溯。 记得当初做这题想了半天电话号码怎么存储,用哈希表就可以解决。 class Solution { public:vector<string> result;string digits;int loc0;unordered_map<char,string> dictionary{{2,"abc"},{3,"def"},{4,…...
kotlin,数字滚动选择
用国内的通义灵码和codegeex都没有弄出来,最后只得用墙外的chatgpt才弄出一个满意的。kotlin真的有点难,好在有AI,让学习没这难了。 package com.example.mynumsetimport android.os.Bundle import androidx.activity.ComponentActivity imp…...
Flask学习笔记 - 数据库
Flask 数据库操作 Flask 提供了多种方式来与数据库进行交互,包括直接使用 SQL 和利用 ORM(对象关系映射)工具,如 SQLAlchemy。 使用SQLAlchemy创建和管理数据库:使用 db.create_all() 创建表。CRUD 操作:…...
学透Spring Boot — 015. 自废武功——关闭自动配置
这是我的《学透Spring Boot》专栏的第15篇文章,了解更多请移步我的专栏:CSDN Postnull的专栏《学透Spring Boot》 目录 遇到的问题 分析日志 自动配置的过程 解决报错 方法1:添加数据库配置 方法2:关闭JPA自动配置 总结 遇…...
DeepSeek本地部署(Ollama)
1. Ollama 安装 Ollama 官网地址: https://ollama.com/安装包网盘地址: https://pan.baidu.com 2. Deepseek 部署 根据自己电脑配置和应用需求选择不同模型,配置不足会导致运行时候卡顿。 版本安装指令模型大小硬盘(存储)显卡…...
Python如何将已经安装的包导出为 .whl 文件以便离线使用
1、确认已安装的依赖包 pip list --formatfreeze > requirements.txt2、下载但不安装依赖包 # 单个包使用 pip download 依赖包名称 -d ./offline_packages# 多个包使用 pip download -r requirements.txt -d ./offline_packages使用离线包的时候,还需要确保在与…...
TypeScript学习第十六篇 - interface和type的区别?
在 TypeScript 中,interface 和 type 都用于定义自定义类型,但它们有一些关键区别: 1. 主要区别 1.1. 语法差异 interface 使用 interface 关键字。 interface Person {name: string;age: number; } type 使用 type 关键字。 type Pers…...
vue-axios跨域问题
vue-axios跨域问题 跨域原因现象前端解决方案 跨域原因 跨域:浏览器从一个域名的网页去请求另一个域名的资源时,域名、端口、协议任一不同,都是跨域。 在前后端分离的模式下,前后端的域名是不一致的,此时就会发生跨域…...