xv6 磁盘中断流程和启动时调度流程
- 首发公号:Rand_cs
本文讲述 xv6 中的一些细节流程,还有对之前文中遗留的问题做一些补充说明,主要有以下几个问题:
- 一次完整的磁盘中断流程
- 进入调度器后的详细流程
- sched 函数中的条件判断
- scheduler 函数中为什么要周期性关中断
一次完整的磁盘流程
此节讲述完整的磁盘读写流程,读写的流程总体差不多,这里以读为例子,先看“流程图”(看代码时的笔记图)

readint $T_SYSCALLsys_readfilereadreadibreadbgetiderwidestart
还是从 A 进程的用户态 read 函数开始:
- A 进程用户态调用 read 读取磁盘上的数据
- read 通过 INT 0x80 软件中断,通过中断门进入内核,此时会关中断(NOTE 这里我以中断门来实现系统调用为例,会关中断,xv6 源代码是以陷阱门实现系统调用,不会关中断)
- 期间多次取锁放锁,进行了多次 pushcli 和 popcli,但总是成对存在,所以目前总体还是处于 0 次 puchcli 状态
- 如果磁盘数据没有缓存,调用 iderw 来读写磁盘
A 进程内核态,iderw 函数:
- acquire(&idelock),获取磁盘锁,pushcli,cpu.IF = 0,1 次pushcli 状态
- 调用 idestart,将要读写的命令,扇区号等信息写进磁盘端口,以此来请求磁盘操作。写磁盘端口是通过 out 指令实现的。向磁盘发送命令后,磁盘就会工作,磁盘完成工作后就会向 cpu 发送中断信号。
- A 进程调用 sleep 等待磁盘操作完成。在 sleep 函数中,获取 ptable.lock,释放 idelock,然后调用 sched 让出
by the way,这里补充说明 sched 函数中的条件检查,之前的文章都一笔带过了:
void sched(void) //让出CPU,重新调度
{int intena;struct proc *p = myproc();if(!holding(&ptable.lock)) // 必须持有 ptable.lockpanic("sched ptable.lock");if(mycpu()->ncli != 1) // 1 次 pushcli 状态panic("sched locks");if(p->state == RUNNING) // 只可能是 SLEEPING、RUNNABLE、ZOMBIE 三种状态之一panic("sched running");if(readeflags()&FL_IF) // 此时肯定处于关中断状态(通过中断门进入内核会关中断,1次pushcli状态也应该对应关中断状态)panic("sched interruptible");intena = mycpu()->intena;swtch(&p->context, mycpu()->scheduler);mycpu()->intena = intena;
}
sched 函数中有 4 个条件检查:
- xv6 是个多 CPU 多任务系统,在 sched 任务调度的时候,需要持有 ptable.lock,不然进程的上下文会发生紊乱,举个简单的例子,A 时间片到了,先将 A 的状态设置为 RUNNABLE,然后调用 sched 让出 CPU,如果此时没有持有 ptable.lock,那么 A 进程便可能在另一个 CPU 上被调度,那么便出现一个进程在两 CPU 上运行的情况,Error
- 在 sched 函数中应当只有 1 次 pushcli 状态,这个条件检查感觉有点难以理解。从实践看代码确实,不管从哪条路径到达 sched 函数,都应该只有 1 次 pushcli,这是获取 ptable.lock 的锁造成的。从个人理解上说,sched 是为了调度进程,是要从 A 进程到 B 进程,那么 A 进程的开关中断(pushcli popcli 次数)不应该带入 B 进程,除了一种情况——调度,那就是 A 进程获取 ptable.lock 但是要在 B 进程中释放 ptable.lock。所以 sched 中应当只有 1 次 pushcli
- 在真正切换进程上下文之前,会首先修改旧进程的状态,在 xv6 中是 SLEEPING、RUNNABLE、ZOMBIE 三种之一
- 在 sched 中理应处于关中断状态,如果是通过中断门进入内核的,那么本身就处于关中断。如果是通过陷阱门进入内核,那么有 1 次 pushcli,也会处于关中断状态
回到磁盘中断,当 A 进程调用 sched 切换到 B 进程,这里假如 B 进程最初是因为时间片到了,调用 yield->sched->swtch 主动让出 CPU 的,则 B 进程的流程如下:
- 回到 B 进程 sched 函数中的 swtch 下一条指令处,然后释放 ptable.lock,此时 cpu.IF = 0,0 次 pushcli 状态,cpu.IF = 0 仍然处于关中断状态 是因为 A 进程通过中断门进入内核关中断造成的
- B 进程经过一些列指令后,最后执行 iret 返回 B 进程的用户态,此时会开中断
- NOTE,这里我们假设 CPU 内部逻辑:每条指令执行后都会检查是否有中断发生,如果有中断发生且开中断的情况下,则去处理中断。再假设,此前的磁盘操作已完成,已经向 CPU 发生了中断信号。但是在此之前 CPU 一直没有去处理中断,是因为在此之前一直处于关中断状态。
- Now,CPU 处于开中断状态,继续执行 B 进程的指令,开中断后的第一条指令执行完成后,检查是否有中断发生,发现有磁盘中断,那么中断 B 进程,通过中断门进入内核(该过程关中断)
- 执行磁盘中断处理程序,也就是执行 insl 指令从 0x1f0 端口将磁盘数据读取到内存,然后唤醒等待该磁盘事件的进程,在我们的例子当中就是 A 进程
- 中断执行完成,iret 返回 B 进程用户态(该过程开中断)
- 继续执行 B 进程的指令
- 时钟中断 B 进程,再次通过中断门进入内核(关中断),发现 B 进程的时间片到了,那么调用 yield->sched->swtch 重新调度进程,这里假设调度到 A 进程
回到 A 进程的内核态,准确来说回到 iderw->sleep->sched->swtch 的下一条指令
- A 进程执行 release(ptable.lock)、acquire(idelock)、release(idelock),此时状态: cpu.IF = 0,0 次 pushcli
- 将磁盘中断获取的数据 cp 到 A 进程内核态
- A 进程层层返回
- 最后 iret 返回 A 进程用户态(开中断)
系统启动进入调度器后的流程

mainuserinit //准备好 initcode 进程mpmainscheduler // 进入调度器
调度器上下文:
- 第一次进入 scheduler,for 循环找到 RUNNABLE 进程,目前就只有一个 initcode 进程为 RUNNABLE 进程,找到并切换上下文到 initcode 进程
initcode 进程上下文:
- 执行 forkret 函数,因为是第一次执行,会首先执行 iinit 来初始化根文件系统
- 执行 readsb 从磁盘中读取超级块数据,期间会使用 iderw 读写磁盘,具体流程见第一小节。总之,initcode 进程会调用 sleep 函数让出 CPU 来等待磁盘操作
- 在 sleep->sched->swtch 中再次切换上下文到 调度器上下文
调度器上下文:
- 切换到内核页表,然后遍历任务队列,寻找 RUNNABLE 进程,但是目前只有一个且处于 SLEEPING 状态的进程,所以这里调度器会轮询空转,直到磁盘中断处理完成,initcode 进程被唤醒。
- 再次切换上下文到 initcode 进程
initcode 进程上下文
- 回到 forkret->iinit->readsb->bread->iderw->sleep->sched->swtch 的下一条指令处,然后层层返回到 forkret
- 再执行 initlog 恢复日志,这里会涉及到两次磁盘读写,道理同上,不再赘述
- 第 4 次调度到 initcode 进程后,forkret 函数执行完毕,再执行 trapret 函数,其中包含了 iret 指令,至此回到用户态,开始执行 initcode 进程的逻辑
by the way again,这里解释为什么在 scheduler 函数中需要周期性的开中断:
void scheduler(void)
{for(;;){sti(); // 周期性开中断for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){ //循环找一个RUNNABLE进程...}}
}
进入调度器上下文有两条路径:
- 系统刚启动进入 scheduler
- sched 函数中 swtch 上下文到调度器
回想前面说的 sched 函数,在它切换到新进程并返回用户态之前理应都处于关中断的状态。而一直处于关中断且没有开中断的话会引发死循环。
举个例子,假设没有周期性的开中断,也就是 scheduler 代码长这样的话:
void scheduler(void)
{for(;;){// 遍历进程列表,寻找 RUNNABLE 进程for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){...}}
}
假如当前系统只有一个进程(shell进程),它需要等待键盘输入而被阻塞(state==SLEEPING),内层循环是找不到 RUNNABLE 进程的,便回到外层循环,外层循环现在相当于什么也不做,便又再次进入内层循环。如此下来死循环。
而加入周期性的开中断后,CPU 便会响应中断。当有键盘输入时,中断当前的调度上下文而进入中断上下文,执行键盘中断处理程序,唤醒 shell 进程,中断处理完成后再回到调度上下文。此时内层循环便能找到一个 RUNNABLE 进程,然后切换到它的上下文执行。
本文就先补充这么多吧,这补充系列的文章是之前做了关于 xv6、nemu 的项目,将 xv6 启动到 nemu 上,这需要对很多地方细扣,对 xv6 的理解又增加了一些,分享出来。
停更这么久啊,一直再忙工作,学习新的东西,时间不是很多,当然也有懒的原因,后面慢慢克服回归吧。OK,那有什么问题欢迎来讨论交流。
- 首发公号:Rand_cs
相关文章:
xv6 磁盘中断流程和启动时调度流程
首发公号:Rand_cs 本文讲述 xv6 中的一些细节流程,还有对之前文中遗留的问题做一些补充说明,主要有以下几个问题: 一次完整的磁盘中断流程进入调度器后的详细流程sched 函数中的条件判断scheduler 函数中为什么要周期性关中断 …...
Spring Security 6.x 系列(6)—— 显式设置和修改登录态信息
一、前言 此篇是对上篇 Spring Security 6.x 系列(5)—— Servlet 认证体系结构介绍 中4.9章节显式调用SecurityContextRepository#saveContext进行详解分析。 二、设置和修改登录态 2.1 登录态存储形式 使用Spring Security框架,认证成功…...
Linux的软件安装
Linux的软件安装 1、rpm软件安装包 RPM(RedHat Package Manager)安装管理 这个机制最早是由Red Hat开发出来,后来实在很好用,因此很多 distributions(发行版)就使用这个机制来作为软件安装的管理方式 。包括Fedora,CentOS,S…...
443. 压缩字符串
这篇文章会收录到 : 算法通关村第十二关-黄金挑战字符串冲刺题-CSDN博客 压缩字符串 描述 : 给你一个字符数组 chars ,请使用下述算法压缩: 从一个空字符串 s 开始。对于 chars 中的每组 连续重复字符 : 如果这一组长度为 1 ,…...
Python面经【6】
Python面经【6】 一、什么是Python的自省机制二、关于Python程序的运行方面,有什么手段可以提升性能三、dict的item()和iteritems()的不同四、说明一个os.path和sys.path分别代表什么五、说一下字典和json的区别六、什么是可变、不可变类型 一、什么是Python的自省机…...
2020年6月9日 Go生态洞察:VS Code Go扩展加入Go项目
🌷🍁 博主猫头虎(🐅🐾)带您 Go to New World✨🍁 🦄 博客首页——🐅🐾猫头虎的博客🎐 🐳 《面试题大全专栏》 🦕 文章图文…...
C语言错误处理之“非局部跳转<setjmp.h>头文件”
目录 前言 setjmp宏 longjmp函数 使用方法: 实例:测试setjmp与longjmp的使用 前言 通常情况下,函数会返回到它被调用的位置,我们无法使用goto语句改变它的返回的方向,因为goto语句只能跳转到同一函数内的某个标号…...
【SpringCloud】微服务架构设计模式
一、聚合气微服务设计模式 最常见、最简单的设计模式,效果如图所示: 聚合器调用多个服务实现应用程序所需的功能 它可以是一个简单的 Web 页面,将检索到的数据进行处理并展示,也可以是一个更高层次的组合微服务,对…...
【EI会议征稿】第三届航空航天工程与系统国际研讨会(ISAES 2024)
第三届航空航天工程与系统国际研讨会(ISAES 2024) 2024 3rd International Symposium on Aerospace Engineering and Systems 第三届航空航天工程与系统国际研讨会将于2024年3月22-24日在南京召开!会议紧密聚焦“航空航天工程”领域的热点和难点问题,…...
唯创知音WT588F02A-16S录音语音芯片在宠物喂食器中的应用:小芯片,大功能
在现代社会中,宠物已经成为人们生活中的一部分,而宠物喂食器作为宠物养护的重要工具,也越来越受到人们的关注。为了满足人们对宠物喂食器的多样化需求,唯创知音的WT588F02A-16S录音芯片在其中发挥着重要作用。 唯创知音的WT588F0…...
SELinux零知识学习三十七、SELinux策略语言之约束(1)
接前一篇文章:SELinux零知识学习三十六、SELinux策略语言之角色和用户(7) 四、SELinux策略语言之约束 SELinux对策略允许的访问提供了更严格的约束机制,不管策略的allow规则如何。 1. 近距离查看访问决定算法 为了理解约束的用途,先来看一下SELinux Linux安全模块(Lin…...
sqli-labs靶场详解(less25/25a-less28/28a)
在SQL注入过程中难点就是判断注入点 只要注入点确定了 获取数据库数据的过程就是复制 从这关开始 只进行判断注入点了和代码逻辑分析了 因为注入操作太简单了(不演示了) 目录 less-25 less-25a less-26 less-26a less-27 less-27a less-28 less-…...
如何优化 Elasticsearch 查询性能
优化 Elasticsearch 查询性能需要从多个方面入手。通过合理的索引设计、优化查询语句、优化硬件资源和集群架构等方面的优化,可以显著提高 Elasticsearch 的查询性能。 1.索引设计优化 良好的索引设计是优化 Elasticsearch 查询性能的关键。可以通过以下几个方面来…...
成功的蓝图:实现长期成长与卓越表现的 6 项策略
能在收入和利润上持续领先同行的公司寥寥无几,不到四分之一。McKinsey的最新研究揭示了这些增长标杆公司与众不同的六大心态和策略。过度谨慎的公司,尤其在动荡时期,也许能捱过当下,但往往无法发掘全部潜力。考虑到近五年历经前所…...
【JavaEE初阶】认识线程、创建线程
1. 认识线程(Thread) 1.1 概念 1) 线程是什么 一个线程就是一个 "执行流". 每个线程之间都可以按照顺序执行自己的代码. 多个线程之间 "同时" 执行着多份代码. 举例: 还是回到我们之前的银⾏的例⼦中。之前我们主要描…...
uniapp中uni.navigateBack返回后刷新页面数据
文章目录 一、前言1.1、[uni.navigateBack](https://uniapp.dcloud.net.cn/api/router.html#navigateback) 二、方法2.1、父页面设置钩子函数onBackPress2.2、uni.$emit和uni.$on监听通知数据变更2.2.1、子页面2.2.2、父页面 2.3、onShow钩子函数处理数据2.3.1、子页面2.3.2、父…...
sed文本 免交互
目录 什么是sed 概念 格式 基本用法 命令的选项 打印第三行 打印日志文件 打印奇数行 打印偶数行 第三行退出 删除第三行 sed在不打开文件的情况下修改文件内容 在后面添加 选项a 在字符中间添加 \n 实现追加换行 全部追加 在前面插入 选项i 替换 选项c …...
轻巧高效的剃须好工具,DOCO黑刃电动剃须刀上手
剃须刀大家都用过,我比较喜欢电动剃须刀,尤其是多刀头的悬浮剃须刀,感觉用起来很方便,剃须效率也很高。最近我在用一款DOCO小蔻的黑刃电动剃须刀,这款剃须刀轻巧易用,而且性价比超高。 相比于同类产品&…...
第15关 K8s HPA:自动水平伸缩Pod,实现弹性扩展和资源优化
------> 课程视频同步分享在今日头条和B站 大家好,我是博哥爱运维,这节课带来k8s的HPA 自动水平伸缩pod( 视频后面有彩蛋 : ) )。 我们知道,初始Pod的数量是可以设置的,同时业务也分流量高峰和低峰&a…...
接口测试工具(Jmeter)必学技巧
安装 使用JMeter的前提需要安装JDK,需要JDK1.7以上版本 目前在用的是JMeter5.2版本,大家可自行下载解压使用 运行 进入解压路径如E: \apache-jmeter-5.2\bin,双击jmeter.bat启动运行 启动后默认为英文版本,可通过Options – Choos…...
线程与协程
1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指:像函数调用/返回一样轻量地完成任务切换。 举例说明: 当你在程序中写一个函数调用: funcA() 然后 funcA 执行完后返回&…...
MMaDA: Multimodal Large Diffusion Language Models
CODE : https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA,它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构…...
Nuxt.js 中的路由配置详解
Nuxt.js 通过其内置的路由系统简化了应用的路由配置,使得开发者可以轻松地管理页面导航和 URL 结构。路由配置主要涉及页面组件的组织、动态路由的设置以及路由元信息的配置。 自动路由生成 Nuxt.js 会根据 pages 目录下的文件结构自动生成路由配置。每个文件都会对…...
Element Plus 表单(el-form)中关于正整数输入的校验规则
目录 1 单个正整数输入1.1 模板1.2 校验规则 2 两个正整数输入(联动)2.1 模板2.2 校验规则2.3 CSS 1 单个正整数输入 1.1 模板 <el-formref"formRef":model"formData":rules"formRules"label-width"150px"…...
算法岗面试经验分享-大模型篇
文章目录 A 基础语言模型A.1 TransformerA.2 Bert B 大语言模型结构B.1 GPTB.2 LLamaB.3 ChatGLMB.4 Qwen C 大语言模型微调C.1 Fine-tuningC.2 Adapter-tuningC.3 Prefix-tuningC.4 P-tuningC.5 LoRA A 基础语言模型 A.1 Transformer (1)资源 论文&a…...
快刀集(1): 一刀斩断视频片头广告
一刀流:用一个简单脚本,秒杀视频片头广告,还你清爽观影体验。 1. 引子 作为一个爱生活、爱学习、爱收藏高清资源的老码农,平时写代码之余看看电影、补补片,是再正常不过的事。 电影嘛,要沉浸,…...
c++第七天 继承与派生2
这一篇文章主要内容是 派生类构造函数与析构函数 在派生类中重写基类成员 以及多继承 第一部分:派生类构造函数与析构函数 当创建一个派生类对象时,基类成员是如何初始化的? 1.当派生类对象创建的时候,基类成员的初始化顺序 …...
深入理解Optional:处理空指针异常
1. 使用Optional处理可能为空的集合 在Java开发中,集合判空是一个常见但容易出错的场景。传统方式虽然可行,但存在一些潜在问题: // 传统判空方式 if (!CollectionUtils.isEmpty(userInfoList)) {for (UserInfo userInfo : userInfoList) {…...
comfyui 工作流中 图生视频 如何增加视频的长度到5秒
comfyUI 工作流怎么可以生成更长的视频。除了硬件显存要求之外还有别的方法吗? 在ComfyUI中实现图生视频并延长到5秒,需要结合多个扩展和技巧。以下是完整解决方案: 核心工作流配置(24fps下5秒120帧) #mermaid-svg-yP…...
命令行关闭Windows防火墙
命令行关闭Windows防火墙 引言一、防火墙:被低估的"智能安检员"二、优先尝试!90%问题无需关闭防火墙方案1:程序白名单(解决软件误拦截)方案2:开放特定端口(解决网游/开发端口不通)三、命令行极速关闭方案方法一:PowerShell(推荐Win10/11)方法二:CMD命令…...
