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

《操作系统》by李治军 | 实验5.pre - switch_to 汇编代码详解

目录

【前言】

一、栈帧的处理

1. 什么是栈帧

2. 为什么要处理栈帧

3. 执行 switch_to 前的内核栈

4. 栈帧处理代码分析

二、PCB 的比较

1. 根据 PCB 判断进程切换与否

2. PCB 比较代码分析

三、PCB 的切换

1. 什么是 PCB 的切换

2. PCB 切换代码分析

四、TSS 内核栈指针的重写

1. 为什么要重写 TSS 中的内核栈

2. 内核栈重写代码分析

五、内核栈的切换

1. 如何完成内核栈切换

2. 内核栈切换代码分析

六、LDT 的切换

1. LDT 切换代码分析

七、用户栈的切换

1. switch_to 退出代码分析


【前言】

       在李治军老师的《操作系统》课程的实验 5(基于内核栈切换的进程切换)中,需要完成 switch_to 函数的汇编代码编写。代码很容易获取,网上的资源非常多,但是拿到了看不懂……

       所以本文章将会对 switch_to 的代码进行逐条分析,希望能帮助理解基于内核栈的进程切换的整体流程。

switch_to() 完整汇编代码:

.align 2
switch_to:// 因为该汇编函数要在c语言中调用,所以要先在汇编中处理栈帧pushl %ebpmovl %esp,%ebppushl %ecxpushl %ebxpushl %eax// 将ebp+8指向的数据(目标进程的PCB)传递给ebx,然后进行判断:// 如果目标进程的pcb <<等于>> 当前进程的pcb => 不需要进行切换,直接退出函数调用// 如果目标进程的pcb <<不等于>> 当前进程的pcb => 需要进行切换,直接跳到下面去执行movl 8(%ebp),%ebxcmpl %ebx,currentje 1f/** 执行到此处,就要进行真正的基于堆栈的进程切换了 **/// 切换PCBmovl %ebx,%eaxxchgl %eax,current// 重写TSS中内核栈的指针movl tss,%ecxaddl $4096,%ebxmovl %ebx,ESP0(%ecx)// 切换内核栈movl %esp,KERNEL_STACK(%eax)movl 8(%ebp),%ebxmovl KERNEL_STACK(%ebx),%esp// 切换LDTmovl 12(%ebp),%ecxlldt %cx// 切换 LDT 之后movl $0x17,%ecxmov %cx,%fs// 这一段先不用管cmpl %eax,last_task_used_mathjne 1fclts// 现在进入新进程的内核栈工作了,所以接下来做的四次弹栈以及ret处理使用的都是新进程内核栈中的东西
1:	popl %eaxpopl %ebxpopl %ecxpopl %ebpret

一、栈帧的处理

1. 什么是栈帧

       大多数 CPU 上的程序实现都是通过使用来支持函数调用操作。栈被用来传递函数参数、存储返回地址、临时保存寄存器原有值以备恢复以及用来存储局部数据。单个函数调用操作所使用的栈部分被称为栈帧结构。
       一个函数栈帧结构的两端由两个指针来指定:① ebp:用作指针(指向栈帧部);② esp:用作指针(指向栈帧部)。在函数执行过程中,肯定会有数据的入栈和出栈,而栈指针 esp 就会随之变化(esp 始终指向栈顶)。因此,函数中对大部分数据的访问都是基于帧指针 ebp 进行的。

>> 强烈推荐先看看这篇文章再继续:栈帧_yxysdcl的博客-CSDN博客

2. 为什么要处理栈帧

       现在我们知道,每个函数的每次调用,都有它自己独立的一个栈帧,这个栈帧中保存着该函数所需要的各种信息。寄存器 ebp 指向当前栈帧的底部,寄存器 esp 指向当前栈帧的顶部。

       当调用一个函数时,就意味着要创建一个属于这个函数自己的栈帧。而进入该函数后这个栈帧就变成了当前栈帧,也就是说退出上一个函数的栈帧进入新的栈帧。所以要让 ebp 重新指向当前栈帧的底部,让 esp 重新指向当前栈帧的顶部,同时还要保存上一个函数的栈帧底部和栈帧顶部。

3. 执行 switch_to 前的内核栈

       现在我们应该清楚,在执行上面的 switch_to 汇编代码前,当前进程内核栈的情况应该如下图所示(我们以在 schedule 中调用 switch_to 为例)。此时还没有进入 switch_to 函数,所以 ebp 和 esp 应该分别指向 schedule 函数的栈帧底部和栈帧顶部(栈帧底部具体在哪儿就不用管了)。

       schedule() 调用 switch_to() 的时候会依次将 switch_to 的参数(从右至左):_LDT(next) 和 pnext,以及 switch_to 的返回地址:{  依次入栈。

4. 栈帧处理代码分析

接下来我们逐条分析 switch_to 中栈帧处理的部分。

pushl %ebp

>> 将 ebp 入栈 <<

       这条指令的目的就是保存 schedule() 的栈帧底部。因为现在进入了 switch_to 函数,也就是说要切换到 switch_to 的栈帧了,所以必须把上面函数的栈帧底部和栈帧顶部保存起来,这样 switch_to 结束返回时才能成功返回到之前调用函数的位置。ps:这里不用另外保存栈顶,因为上一个栈帧的顶部就是下一个的栈帧的底部(两栈帧相邻)。

所以 ebp 入栈后内核栈变为:(这里 switch_to 的栈帧底部和顶部是一样的)

movl %esp,%ebp

>> 将 esp 中内容传递给 ebp <<

       原来 ebp 指向 schedule 函数的栈帧底部,但这句代码执行完后,ebp 和 esp 就都指向刚刚压入的 ebp 位置,也就是 switch_to 函数的栈帧底部。

pushl %ecx

pushl %ebx

pushl %eax

>> 将 ecx、ebx、eax 依次入栈 <<

       入栈后内核栈如下图所示。可以看到现在 ebp 指向 switch_to() 栈帧底部,esp 指向 switch_to()栈帧顶部,而且上一个函数 schedule 的栈帧底部指针就保存在调用的 switch_to 函数的栈帧底部位置,之后 switch_to 结束时就要通过这个 ebp 返回 schedule。现在栈帧就处理完毕了!

二、PCB 的比较

1. 根据 PCB 判断进程切换与否

① 目标进程的 PCB  =  当前进程的 PCB  =>  无需进行切换,直接退出函数调用

② 目标进程的 PCB    当前进程的 PCB  =>  需要进行切换,接着进行切换操作

2. PCB 比较代码分析

movl 8(%ebp),%ebx

>> 将 ebp 指针 + 8 指向的数据传递给了 ebx 寄存器 <<

       Linux 0.11 内核栈的地址顺序从上往下,是由高到低的。所以 ebp + 8 指向的就是 pnext(目标进程的 PCB ),所以 ebx 现在就存储着 pnext。

cmpl %ebx,current
je 1f

>> 比较 ebx 中的内容和 current <<

ebx 中保存着目标进程的 PCB,current 是当前进程的 PCB。

① 如果两个进程的 PCB 相同,则跳转到 1f 位置处,switch_to 接下来的代码也不用执行,不会进行进程切换。

② 如果两个进程的 PCB 不同,则继续执行 switch_to 接下来的代码进行进程切换。

三、PCB 的切换

1. 什么是 PCB 的切换

       切换 PCB 就是要让 current 切换为目标进程的 PCB。

2. PCB 切换代码分析

movl %ebx,%eax

xchgl %eax,current

>> 将 ebx 中数据置给 eax ,再交换 eax 和 current 的内容 <<

       执行前 ebx 中保存着 pnext(目标进程的 PCB),current 是当前进程的 PCB。这两句代码执行后,ebx 和 current 都指向目标进程的 PCB,eax 则指向当前进程的 PCB。PCB 切换完成!

四、TSS 内核栈指针的重写

1. 为什么要重写 TSS 中的内核栈

       执行 INT 0x80 中断之后,进程进入内核,这时要先找到内核栈的位置,将用户态下的 SS:ESP、CS:EIP、EFLAGS 都压入内核栈中保存下来,实现用户栈到内核栈的切换。以上的寻找内核栈和压栈操作都是系统自动完成的。也就是说系统会根据一些硬件寄存器(TR)知道这个哪个进程,以及该进程对应的内核栈在哪里,同时将用户态下的 SS:ESP、CS:EIP、EFLAGS 都压入内核栈中保存下来。

       虽然此时不再使用 TSS 进行进程切换,但是 Intel 的中断处理机制还是要保持,因为中断机制就是通过 TR 指向的 TSS 来找到当前进程的内核栈,并自动将用户栈等相关信息压入对应内核栈。也就是说,找到内核栈还得依靠 TR 指向的当前 TSS。所以每个进程仍需要一个TSS,这样系统才能并通过 TSS 中的内核栈指针 esp0 找到当前进程的内核栈并进行压栈操作。

       这里采用的方案是让所有进程共用一个TSS(即 0 号进程的TSS),并且这个 TSS 指向当前进程。在 sched.c 中定义的全局变量  struct tss_struct *tss = &(init_task.task.tss);  就是 0 号进程的 TSS,所有的进程都共用这个 TSS,任务切换时再发生变化。

这个唯一的 TSS 的目的就是:在中断处理时,帮助 CPU 找到当前进程的内核栈的位置

2. TSS 内核栈重写代码分析

movl tss,%ecx

>> 将 tss(TR 指向的 TSS)赋给 ecx 寄存器 <<

所以现在 ecx 也保存了当前进程的 TSS。

addl $4096,%ebx

>> ebx + 4096 <<

       ebx 本来指向目标进程的 PCB,执行该指令后,ebx 就指向目标进程的内核栈。为什么就指向内核栈了?因为 Linux 0.11 中进程的 PCB内核栈同一页内存上(即一块 4KB 大小的内存)。其中 PCB 位于这页内存的低地址,内核栈位于这页内存的高地址。也就是说,低地址空间 base 用来存放进程的 PCB,而 base + PAGE_SIZE 则作为该进程的内核栈的栈底。

       为什么偏移量是 4096 ?因为 4096 = 4KB = PAGE_SIZE,所以 ebx 加 4096 就可以得到内核栈的地址。

movl %ebx,ESP0(%ecx)

>> 将 ebx 中内容(目标进程的内核栈地址)复制到 ecx + ESP0 指向的位置 <<

       ecx 指向当前进程的 TSS,而 ESP0 = 4。我们再看 tss_struct 的定义,发现偏移为 4 的地方就是 TSS 中的内核栈指针 esp0,所以 ecx + ESP0 对应位置就是 tss 中的内核栈指针 esp0。将目标进程的内核栈地址赋给 esp0,实现了 tss 中内核栈指针的重写!

五、内核栈的切换

1. 如何完成内核栈切换

       上一步只是修改了 tss 中的内核栈指针 esp0,在进入内核时帮助 CPU 找到当前进程的内核栈以进行相关入栈操作,但并没有实际切换内核栈,因为 esp 还是指向当前进程的内核栈栈顶。要判断目前位于哪个进程的内核栈中,就看 esp 指向哪儿。

       所以完成内核栈的切换非常简单,就是将寄存器 esp(内核栈使用到当前情况时的栈顶位置)的值保存到当前进程 PCB 中的对应位置,再从目标进程 PCB 中的对应位置取出保存的内核栈栈顶放入 esp 寄存器中。这样处理完后,再通过 esp 使用内核栈时使用的就是目标进程的内核栈了。

2. 内核栈切换代码分析

movl %esp,KERNEL_STACK(%eax)

>> 将 esp 中内容保存到 eax + KERNEL_STACK 位置 <<

       eax 指向当前进程的 PCB(如果忘了可以返回 “PCB 的切换” 去看看),而 KERNEL_STACK 的数值没有明确定义,因为 Linux 0.11 中 PCB 的定义里并没有保存内核栈指针这个域(kernelstack),所以需要我们自己找位置加上这个定义,而宏 KERNEL_STACK 就是我们添加的那个位置。添加位置不同,KERNEL_STACK 的值也会不同。

       但是不管 KERNEL_STACK 的值是什么,eax + KERNEL_STACK 就是当前进程的 PCB 中对应存储内核栈指针的位置。所以这条指令实现了将寄存器 esp(内核栈使用到当前情况时的栈顶位置)的值保存到当前进程 PCB 中的对应位置。

movl 8(%ebp),%ebx

>> 将 ebp 指针 + 8 指向的数据传递给了 ebx 寄存器 <<

ebp + 8 指向的还是 pnext(目标进程的 PCB ),所以现在 ebx 存储着目标进程的 PCB。

movl KERNEL_STACK(%ebx),%esp

>> 将 ebx + KERNEL_STACK 位置的内容保存到 esp 中 <<

       ebx + KERNEL_STACK 位置的内容就是目标进程的 PCB 中对应存储内核栈指针的位置,所以这条指令实现了从目标进程 PCB 中的对应位置取出保存好的内核栈栈顶放入 esp 寄存器中,现在的 esp 就指向新进程的内核栈栈顶了ebp 指向的还是原来进程的内核栈(假设从进程 A 切换到进程 B)。到此正式完成了内核栈的切换!

六、LDT 的切换

1. LDT 切换代码分析

movl 12(%ebp), %ecx

>> 将 ebp 指针 + 12 指向的数据传递给了 ecx 寄存器 <<

       现在的内核栈(进程 A)中数据如下图所示。注意现在 ebp 指向的还是原来进程的内核栈,但 esp 指向的是新进程的内核栈。所以 ebp + 12 指向的就是 _LDT(next)(目标进程 - 进程 B 的 LDT ),这条指令就是负责取出对应 LDT(next) 的那个参数,这里暂时不用深入理解。

lldt %cx

       这条指令负责修改 LDTR 寄存器。一旦完成了修改,下一个进程在执行用户态程序时使用的映射表就是自己的 LDT 表了,地址空间就实现了分离。

movl $0x17,%ecx

mov %cx,%fs

       这两条指令在 LDT 切换完成之后,作用是重新取一下段寄存器 fs 的值。这两句指令必须要加、且必须出现在切换完 LDT 之后。因为 fs 的作用——通过 fs 访问进程的用户态内存,而 LDT 切换完成就意味着切换了分配给进程的用户态内存地址空间,所以前一个 fs 指向的是上一个进程的用户态内存,而现在需要执行下一个进程的用户态内存,所以需要用这两条指令来重取 fs。

七、用户栈的切换

1. switch_to 退出代码分析

cmpl %eax,last_task_used_math
jne 1f
clts

在 PCB、内核栈和 LDT 切换完成之后还有上面这段代码,我们暂时忽略不管,直接来到最后的 4 条出栈指令和 ret :

popl %eax
popl %ebx
popl %ecx
popl %ebp
ret

上面的 5 条指令执行前内核栈情况如图所示:

       可以看出进程 A 和进程 B 的内核栈整体结构是差不多的。因为任何一个进程进入内核,用户态下的 SS:ESP、CS:EIP、EFLAGS 等都会被自动压入内核栈中,所以内核栈栈底的内容都是该进程用户态下的相关信息(SS ~ CS)。而进程需要切换时通过 schedule() 进行调度找到目标进程,schedule() 退出前又调用了 switch_to 进行进程切换,这段过程对每个进程都是一样。所以从调用 switch_to 开始,_LDT(next) ~ eax 就依次入栈,每个进程都如此。

       回到这 5 条指令,此时已经切换了PCB、内核栈、LDT,剩用户栈还没有切换(通过 iret 实现)。esp 指向新进程的内核栈栈顶,但 ebp 还指向原来进程的内核栈。经过前 3 条出栈指令后,内核栈中情况如图所示:

popl %eax
popl %ebx
popl %ecx

       此时 esp 指向的进程 B 内核栈栈顶的 ebp 是什么?就是当前进程——进程 B 上一次调用 schedule() 时 schedule 的栈帧底部!如果忘了可以返回 “栈帧的处理” 看一看。现在继续执行下一条指令:

popl %ebp

执行后内核栈情况如下图所示,也就是回到了当时进程 B 停止时调用 schedule 的状态。

现在执行最后一条指令:

ret

       这条指令就是 switch_to 的返回指令,执行之后就会弹出 schedule() 的  }  并执行,而这个 是 schedule() 的返回指令。之后进程 B 继续在内核中运行一段代码后,就会退出内核(因为系统调用只是进入内核溜达一圈)。

       进入内核通过 INT 0x80,退出内核就通过 iret,这个 iret 指令就实现用户栈的切换。iret 会执行一系列的操作,其中源 CS ~ 源 SS 也会出栈。而 SS、SP 指向用户栈,就切换到了进程 B 的用户栈,接下来就从进程 B 停止时的位置开始继续往下执行。至此就完成了进程 A 到进程 B 的全部切换。

相关文章:

《操作系统》by李治军 | 实验5.pre - switch_to 汇编代码详解

目录 【前言】 一、栈帧的处理 1. 什么是栈帧 2. 为什么要处理栈帧 3. 执行 switch_to 前的内核栈 4. 栈帧处理代码分析 二、PCB 的比较 1. 根据 PCB 判断进程切换与否 2. PCB 比较代码分析 三、PCB 的切换 1. 什么是 PCB 的切换 2. PCB 切换代码分析 四、TSS 内核…...

c++11基础

文章目录&#xff1a; c11简介统一的列表初始化{}初始化std::initializer_list 声明autodecltypenullptr 范围for循环STL中的一些变化arrayforward_listunordered_map和unordered_set 字符串转换函数 c11简介 在2003年C标准委员会曾经提交了一份技术勘误表(简称TC1)&#xff0…...

Linux:centos:修改临时ip永久ip

使用 ifconfig 查看网卡信息以及ip 临时配置ip 找到要修改ip的网卡的名称&#xff08;我这里使用名称为&#xff1a;ens33网卡&#xff09; # ifconfig 网卡名 ip /子网掩码 ifconfig ens33 192.168.1.2/24 配置永久ip 去配置网卡文件 vi /vim 或 nano vim /etc/s…...

如何真正开启docker远程访问2375

注意看官方文档 Configure remote access for Docker daemon | Docker Documentation 1. windows上Docker Desktop开启远程访问端口2375 系统版本&#xff1a; win10专业版 Docker Desktop版本&#xff1a;4.18.0 很简单勾上&#xff0c; 应用并重启即可 2. linux上开启 尝…...

nodejs连接mysql

npm i express #node后端框架npm i corsnpm i mysqlconst app require(express)(); const cors require(cors); const port 5000; const mysql require(mysql) //引入mysql 模块app.use(cors({}))const conn mysql.createConnection({user: root,password: qwertyuiop…...

异构跨库数据同步还在用Datax?来看看这几个开源的同步方案

在遇到跨库或者异库数据同步时&#xff0c;我们一般都会借助ETL工具来实现数据同步功能。比如目前大家较为熟知的Kettle和Datax。但是&#xff0c;这两个需要定时去查询数据库的数据&#xff0c;会存在一定的延迟&#xff0c;而且&#xff0c;默认采用全量同步的方式&#xff0…...

msvcp140.dll丢失怎么办?msvcp140.dll重新安装的解决方法

msvcp140.dll是微软编译器系统中的一个动态链接库文件&#xff0c;它存储了许多的代码和数据&#xff0c;能帮助计算机程序正常运行。当系统中出现了msvcp140.dll丢失的情况时&#xff0c;则会出现程序无法正常运行的错误。这篇文章将为大家介绍如何解决msvcp140.dll丢失的问题…...

mysql超全语法大全

mysql安装教程 一、登录&#xff08;使用可视化工具&#xff0c;可忽略&#xff09; 打开命令行工具&#xff0c;输入以下命令&#xff0c;根据提示输入 root 用户的密码。 mysql -u root -p mysql -u root -p -D 数据库名二、创建数据库 显示数据库&#xff1a;SHOW DATAB…...

【VR】手柄定位技术

1. 关于Quest Pro头显、控制器的规格分析&#xff08;终篇&#xff09;及Quest 3分辨率 &#xff08;2022年07月29日&#xff09;被认为是“Quest Pro”的高端一体机Project Cambria将于今年秋季正式发布。对于一直关注和分享所述设备情报的YouTuber布拉德利林奇&#xff08;B…...

TDengine 启动 taosAdapter,提供基于6041端口的RESTful 接口,建立REST 连接

一、前文 TDengine 入门教程——导读 二、开发指南 TDengine 完整的软件包包括&#xff1a; 服务端&#xff08;taosd&#xff09;&#xff1b;命令行程序 (CLI&#xff0c;taos) 和一些工具软件&#xff1b;用于与第三方系统对接并提供 RESTful 接口的 taosAdapter&#xff1…...

SY8205同步降压DCDC可调电源模块(原理图和PCB)

SY8205同步buck降压电源模块&#xff0c;输入电压4.5-30V&#xff0c;输出电压0.6-30V可调&#xff0c;效率90%以上&#xff0c;最大连续输出电流5A&#xff0c;峰值电流6A。 开源链接&#xff1a;https://url.zeruns.tech/obGu3 SY8025数据手册下载地址&#xff1a;https://…...

电装光庭汽车电子(武汉)有限公司

电装光庭汽车电子&#xff08;武汉&#xff09;有限公司 &#xff08;汽车座舱显示系统&#xff0c;汽车电子产品及其材料和组件的开发&#xff0c;设计&#xff0c;制造&#xff0c;销售&#xff0c;批发&#xff0c;进出口&#xff09; 一、公司介绍 电装光庭汽车电子是一…...

2023年DAMA-CDGA/CDGP认证合肥/厦门/长春/深圳可以报名

DAMA认证为数据管理专业人士提供职业目标晋升规划&#xff0c;彰显了职业发展里程碑及发展阶梯定义&#xff0c;帮助数据管理从业人士获得企业数字化转型战略下的必备职业能力&#xff0c;促进开展工作实践应用及实际问题解决&#xff0c;形成企业所需的新数字经济下的核心职业…...

android 12.0launcher3中workspace app列表页不显示某个app图标

1.概述 在12.0的开发中,Launcher3 workspace的app列表页 会负责加载系统中app的所有图标 但针对某个不需要显示在桌面的app图标需要过滤掉 所以需要在加载和更新的时候过滤 需要更改两处地方,一处是 加在列表时 一处是安装卸载app 更新app列表时 2.launcher3中workspace ap…...

Java 编写websocket client 压测脚本

对于Java开发者而言&#xff0c;使用Java编写websocket client压测脚本是一件比较容易的事情。下面给出一个基于Java语言的websocket client压测脚本示例&#xff0c;供大家参考。 import java.net.URI; import java.util.concurrent.CountDownLatch; import org.java_websocke…...

设计模式之【备忘录模式】,“后悔药”是可以有的

文章目录 一、什么是备忘录模式1、备忘录模式使用场景2、备忘录模式优缺点3、备忘录模式的三大角色4、白箱备忘录和黑箱备忘录5、思考&#xff1a;备份频率快&#xff0c;备份对象大的备忘录应该如何设计 二、实例1、备忘录模式的一般写法2、使用栈管理富文本编辑器3、游戏状态…...

ATECLOUD云测试平台新能源电机测试系统:高效、可扩展的测试利器

随着全球对环境保护的日益重视&#xff0c;新能源的发展越来越受到关注。电动汽车作为新能源领域的重要组成部分&#xff0c;其性能和质量对于消费者来说至关重要。为了确保电动汽车的性能和质量&#xff0c;测试系统平台解决方案变得越来越重要。本文将介绍一种基于ATECLOUD智…...

项目随机问题笔记

一、前端项目启动的命令 启动项目依赖&#xff1a;npm install 安装cross-env模块&#xff1a;npm i cross-env --save-dev 启动报错时试试这个 npm install node-sass (安装sass) 启动项目命令1 npm run dev 启动项目命令2 npm run start 启动项目命令3 npm start 二、前…...

Linux网络编程之recv函数

功能 recv 函数的功能就是从套接字中接收数据。 头文件 #include <sys/types.h> #include <sys/socket.h>原型 ssize_t recv(int sockfd, void *buf, size_t len, int flags);参数 参数描述sockfdsocket 文件描述符buf接收数据缓冲区len接收数据缓冲区的大小f…...

ChatGPT免费使用的方法有哪些?

目录 一、ChatGpt是什么&#xff1f; 二、ChatGPT国内免费使用的方法&#xff1a; 第一点&#xff1a;电脑端 第二点&#xff1a;手机端 三、结语&#xff1a; 一、ChatGpt是什么&#xff1f; ChatGPt是美国OpenAI [1] 研发的聊天机器人程序 。更是人工智能技术驱动的自然语…...

React Hooks 指南:何时使用 useEffect ?

在 React 的函数组件中&#xff0c;useEffect Hook 是一个强大且不可或缺的工具。它允许我们处理副作用 (side effects)——那些在组件渲染之外发生的操作。但是&#xff0c;什么时候才是使用 useEffect 的正确时机呢&#xff1f;让我们深入探讨一下&#xff01; 什么是副作用…...

金融预测模型开发:数据预处理、机器学习预测与交易策略优化

金融预测模型开发:数据预处理、机器学习预测与交易策略优化 概述 本文将详细介绍一个完整的金融预测模型开发流程,包含数据预处理、机器学习预测和交易策略优化三个核心模块。我们使用Python实现一个端到端的解决方案,适用于股票价格预测和量化交易策略开发。 # 导入必要…...

C++性能优化指南

思维导图&#xff08;转载&#xff09; https://www.processon.com/view/5e5b3fc5e4b03627650b1f42 第 1 章 优化概述 1.1 优化是软件开发的一部分 优化更像是一门实验科学。 1.2 优化是高效的 1.3 优化是没有问题的 **90/10 规则&#xff1a;**程序中只有 10% 的代码…...

uni-app学习笔记二十四--showLoading和showModal的用法

showLoading(OBJECT) 显示 loading 提示框, 需主动调用 uni.hideLoading 才能关闭提示框。 OBJECT参数说明 参数类型必填说明平台差异说明titleString是提示的文字内容&#xff0c;显示在loading的下方maskBoolean否是否显示透明蒙层&#xff0c;防止触摸穿透&#xff0c;默…...

深入浅出玩转物联网时间同步:基于BC260Y的NTP实验与嵌入式仿真教学革命

在万物互联的时代&#xff0c;精准的时间戳是物联网系统的神经节拍器&#xff0c;而NTP协议正是维持这一节律的核心技术。 一、时间同步&#xff1a;物联网世界的隐形基石 在智慧城市、工业4.0等场景中&#xff0c;分散的设备需要毫秒级的时间协同。网络时间协议&#xff08;N…...

LLMs之Structured Output:vLLM 结构化输出指南—从约束生成到自动解析与高效实现

LLMs之Structured Output&#xff1a;vLLM 结构化输出指南—从约束生成到自动解析与高效实现 导读&#xff1a;随着大语言模型&#xff08;LLM&#xff09;在各类任务中的广泛应用&#xff0c;如何使其输出具备可控性、结构化与可解析性&#xff0c;成为实际部署中的关键问题。…...

Azure 虚拟机端口资源:专用 IP 和公共 IP Azure Machine Learning 计算实例BUG

## 报错无解 找不到Azure ML 计算实例关联的 NSG .env 文件和 ufw status&#xff1a; .env 文件中 EXPOSE_NGINX_PORT8080 是正确的&#xff0c;它告诉 docker-compose.yaml 将 Nginx 暴露在宿主机的 8080 端口。 sudo ufw status 显示 Status: inactive&#xff0c;意味着宿…...

使用 Redisson 实现分布式锁—解决方案详解

Redisson 是 Redis 官方推荐的 Java 客户端&#xff0c;提供了一系列分布式服务实现&#xff0c;其中分布式锁是其核心功能之一。本文将深入解析 Redisson 分布式锁的实现原理、高级特性和最佳实践。 一、Redisson 分布式锁的优势 与传统实现的对比 特性手动实现Redisson 实现…...

三十五、面向对象底层逻辑-Spring MVC中AbstractXlsxStreamingView的设计

在Web应用开发中&#xff0c;大数据量的Excel导出功能是常见需求。传统Apache POI的XSSF实现方式在处理超大数据集时&#xff0c;会因全量加载到内存导致OOM&#xff08;内存溢出&#xff09;问题。Spring MVC提供的AbstractXlsxStreamingView通过流式处理机制&#xff0c;有效…...

解决Zotero翻译插件Zotero PDF Translate无法正常翻译

试了很多方法了&#xff0c;不管怎么样还是报错&#xff0c;找到最简单的解决办法&#xff0c;把翻译引擎改成CNJI学术翻译就可以了。 不能用的原因是google 翻译API 无法调用。...