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

xv6 磁盘中断流程和启动时调度流程

  • 首发公号:Rand_cs

本文讲述 xv6 中的一些细节流程,还有对之前文中遗留的问题做一些补充说明,主要有以下几个问题:

  1. 一次完整的磁盘中断流程
  2. 进入调度器后的详细流程
  3. sched 函数中的条件判断
  4. scheduler 函数中为什么要周期性关中断

一次完整的磁盘流程

此节讲述完整的磁盘读写流程,读写的流程总体差不多,这里以读为例子,先看“流程图”(看代码时的笔记图)

请添加图片描述

readint $T_SYSCALLsys_readfilereadreadibreadbgetiderwidestart

还是从 A 进程的用户态 read 函数开始:

  1. A 进程用户态调用 read 读取磁盘上的数据
  2. read 通过 INT 0x80 软件中断,通过中断门进入内核,此时会关中断(NOTE 这里我以中断门来实现系统调用为例,会关中断,xv6 源代码是以陷阱门实现系统调用,不会关中断)
  3. 期间多次取锁放锁,进行了多次 pushcli 和 popcli,但总是成对存在,所以目前总体还是处于 0 次 puchcli 状态
  4. 如果磁盘数据没有缓存,调用 iderw 来读写磁盘

A 进程内核态,iderw 函数:

  1. acquire(&idelock),获取磁盘锁,pushcli,cpu.IF = 0,1 次pushcli 状态
  2. 调用 idestart,将要读写的命令,扇区号等信息写进磁盘端口,以此来请求磁盘操作。写磁盘端口是通过 out 指令实现的。向磁盘发送命令后,磁盘就会工作,磁盘完成工作后就会向 cpu 发送中断信号。
  3. 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 个条件检查:

  1. xv6 是个多 CPU 多任务系统,在 sched 任务调度的时候,需要持有 ptable.lock,不然进程的上下文会发生紊乱,举个简单的例子,A 时间片到了,先将 A 的状态设置为 RUNNABLE,然后调用 sched 让出 CPU,如果此时没有持有 ptable.lock,那么 A 进程便可能在另一个 CPU 上被调度,那么便出现一个进程在两 CPU 上运行的情况,Error
  2. 在 sched 函数中应当只有 1 次 pushcli 状态,这个条件检查感觉有点难以理解。从实践看代码确实,不管从哪条路径到达 sched 函数,都应该只有 1 次 pushcli,这是获取 ptable.lock 的锁造成的。从个人理解上说,sched 是为了调度进程,是要从 A 进程到 B 进程,那么 A 进程的开关中断(pushcli popcli 次数)不应该带入 B 进程,除了一种情况——调度,那就是 A 进程获取 ptable.lock 但是要在 B 进程中释放 ptable.lock。所以 sched 中应当只有 1 次 pushcli
  3. 在真正切换进程上下文之前,会首先修改旧进程的状态,在 xv6 中是 SLEEPING、RUNNABLE、ZOMBIE 三种之一
  4. 在 sched 中理应处于关中断状态,如果是通过中断门进入内核的,那么本身就处于关中断。如果是通过陷阱门进入内核,那么有 1 次 pushcli,也会处于关中断状态

回到磁盘中断,当 A 进程调用 sched 切换到 B 进程,这里假如 B 进程最初是因为时间片到了,调用 yield->sched->swtch 主动让出 CPU 的,则 B 进程的流程如下:

  1. 回到 B 进程 sched 函数中的 swtch 下一条指令处,然后释放 ptable.lock,此时 cpu.IF = 0,0 次 pushcli 状态,cpu.IF = 0 仍然处于关中断状态 是因为 A 进程通过中断门进入内核关中断造成的
  2. B 进程经过一些列指令后,最后执行 iret 返回 B 进程的用户态,此时会开中断
  3. NOTE,这里我们假设 CPU 内部逻辑:每条指令执行后都会检查是否有中断发生,如果有中断发生且开中断的情况下,则去处理中断。再假设,此前的磁盘操作已完成,已经向 CPU 发生了中断信号。但是在此之前 CPU 一直没有去处理中断,是因为在此之前一直处于关中断状态。
  4. Now,CPU 处于开中断状态,继续执行 B 进程的指令,开中断后的第一条指令执行完成后,检查是否有中断发生,发现有磁盘中断,那么中断 B 进程,通过中断门进入内核(该过程关中断)
  5. 执行磁盘中断处理程序,也就是执行 insl 指令从 0x1f0 端口将磁盘数据读取到内存,然后唤醒等待该磁盘事件的进程,在我们的例子当中就是 A 进程
  6. 中断执行完成,iret 返回 B 进程用户态(该过程开中断)
  7. 继续执行 B 进程的指令
  8. 时钟中断 B 进程,再次通过中断门进入内核(关中断),发现 B 进程的时间片到了,那么调用 yield->sched->swtch 重新调度进程,这里假设调度到 A 进程

回到 A 进程的内核态,准确来说回到 iderw->sleep->sched->swtch 的下一条指令

  1. A 进程执行 release(ptable.lock)、acquire(idelock)、release(idelock),此时状态: cpu.IF = 0,0 次 pushcli
  2. 将磁盘中断获取的数据 cp 到 A 进程内核态
  3. A 进程层层返回
  4. 最后 iret 返回 A 进程用户态(开中断)

系统启动进入调度器后的流程

请添加图片描述

mainuserinit //准备好 initcode 进程mpmainscheduler  // 进入调度器

调度器上下文:

  1. 第一次进入 scheduler,for 循环找到 RUNNABLE 进程,目前就只有一个 initcode 进程为 RUNNABLE 进程,找到并切换上下文到 initcode 进程

initcode 进程上下文:

  1. 执行 forkret 函数,因为是第一次执行,会首先执行 iinit 来初始化根文件系统
  2. 执行 readsb 从磁盘中读取超级块数据,期间会使用 iderw 读写磁盘,具体流程见第一小节。总之,initcode 进程会调用 sleep 函数让出 CPU 来等待磁盘操作
  3. 在 sleep->sched->swtch 中再次切换上下文到 调度器上下文

调度器上下文:

  1. 切换到内核页表,然后遍历任务队列,寻找 RUNNABLE 进程,但是目前只有一个且处于 SLEEPING 状态的进程,所以这里调度器会轮询空转,直到磁盘中断处理完成,initcode 进程被唤醒。
  2. 再次切换上下文到 initcode 进程

initcode 进程上下文

  1. 回到 forkret->iinit->readsb->bread->iderw->sleep->sched->swtch 的下一条指令处,然后层层返回到 forkret
  2. 再执行 initlog 恢复日志,这里会涉及到两次磁盘读写,道理同上,不再赘述
  3. 第 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进程...}}
}

进入调度器上下文有两条路径:

  1. 系统刚启动进入 scheduler
  2. 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 磁盘中断流程和启动时调度流程

首发公号&#xff1a;Rand_cs 本文讲述 xv6 中的一些细节流程&#xff0c;还有对之前文中遗留的问题做一些补充说明&#xff0c;主要有以下几个问题&#xff1a; 一次完整的磁盘中断流程进入调度器后的详细流程sched 函数中的条件判断scheduler 函数中为什么要周期性关中断 …...

Spring Security 6.x 系列(6)—— 显式设置和修改登录态信息

一、前言 此篇是对上篇 Spring Security 6.x 系列&#xff08;5&#xff09;—— Servlet 认证体系结构介绍 中4.9章节显式调用SecurityContextRepository#saveContext进行详解分析。 二、设置和修改登录态 2.1 登录态存储形式 使用Spring Security框架&#xff0c;认证成功…...

Linux的软件安装

Linux的软件安装 1、rpm软件安装包 RPM&#xff08;RedHat Package Manager&#xff09;安装管理 ​ 这个机制最早是由Red Hat开发出来,后来实在很好用,因此很多 distributions&#xff08;发行版&#xff09;就使用这个机制来作为软件安装的管理方式 。包括Fedora,CentOS,S…...

443. 压缩字符串

这篇文章会收录到 : 算法通关村第十二关-黄金挑战字符串冲刺题-CSDN博客 压缩字符串 描述 : 给你一个字符数组 chars &#xff0c;请使用下述算法压缩&#xff1a; 从一个空字符串 s 开始。对于 chars 中的每组 连续重复字符 &#xff1a; 如果这一组长度为 1 &#xff0c;…...

Python面经【6】

Python面经【6】 一、什么是Python的自省机制二、关于Python程序的运行方面&#xff0c;有什么手段可以提升性能三、dict的item()和iteritems()的不同四、说明一个os.path和sys.path分别代表什么五、说一下字典和json的区别六、什么是可变、不可变类型 一、什么是Python的自省机…...

2020年6月9日 Go生态洞察:VS Code Go扩展加入Go项目

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…...

C语言错误处理之“非局部跳转<setjmp.h>头文件”

目录 前言 setjmp宏 longjmp函数 使用方法&#xff1a; 实例&#xff1a;测试setjmp与longjmp的使用 前言 通常情况下&#xff0c;函数会返回到它被调用的位置&#xff0c;我们无法使用goto语句改变它的返回的方向&#xff0c;因为goto语句只能跳转到同一函数内的某个标号…...

【SpringCloud】微服务架构设计模式

一、聚合气微服务设计模式 最常见、最简单的设计模式&#xff0c;效果如图所示&#xff1a; 聚合器调用多个服务实现应用程序所需的功能 它可以是一个简单的 Web 页面&#xff0c;将检索到的数据进行处理并展示&#xff0c;也可以是一个更高层次的组合微服务&#xff0c;对…...

【EI会议征稿】第三届航空航天工程与系统国际研讨会(ISAES 2024)

第三届航空航天工程与系统国际研讨会(ISAES 2024) 2024 3rd International Symposium on Aerospace Engineering and Systems 第三届航空航天工程与系统国际研讨会将于2024年3月22-24日在南京召开&#xff01;会议紧密聚焦“航空航天工程”领域的热点和难点问题&#xff0c;…...

唯创知音WT588F02A-16S录音语音芯片在宠物喂食器中的应用:小芯片,大功能

在现代社会中&#xff0c;宠物已经成为人们生活中的一部分&#xff0c;而宠物喂食器作为宠物养护的重要工具&#xff0c;也越来越受到人们的关注。为了满足人们对宠物喂食器的多样化需求&#xff0c;唯创知音的WT588F02A-16S录音芯片在其中发挥着重要作用。 唯创知音的WT588F0…...

SELinux零知识学习三十七、SELinux策略语言之约束(1)

接前一篇文章:SELinux零知识学习三十六、SELinux策略语言之角色和用户(7) 四、SELinux策略语言之约束 SELinux对策略允许的访问提供了更严格的约束机制,不管策略的allow规则如何。 1. 近距离查看访问决定算法 为了理解约束的用途,先来看一下SELinux Linux安全模块(Lin…...

sqli-labs靶场详解(less25/25a-less28/28a)

在SQL注入过程中难点就是判断注入点 只要注入点确定了 获取数据库数据的过程就是复制 从这关开始 只进行判断注入点了和代码逻辑分析了 因为注入操作太简单了&#xff08;不演示了&#xff09; 目录 less-25 less-25a less-26 less-26a less-27 less-27a less-28 less-…...

如何优化 Elasticsearch 查询性能

优化 Elasticsearch 查询性能需要从多个方面入手。通过合理的索引设计、优化查询语句、优化硬件资源和集群架构等方面的优化&#xff0c;可以显著提高 Elasticsearch 的查询性能。 1.索引设计优化 良好的索引设计是优化 Elasticsearch 查询性能的关键。可以通过以下几个方面来…...

成功的蓝图:实现长期成长与卓越表现的 6 项策略

能在收入和利润上持续领先同行的公司寥寥无几&#xff0c;不到四分之一。McKinsey的最新研究揭示了这些增长标杆公司与众不同的六大心态和策略。过度谨慎的公司&#xff0c;尤其在动荡时期&#xff0c;也许能捱过当下&#xff0c;但往往无法发掘全部潜力。考虑到近五年历经前所…...

【JavaEE初阶】认识线程、创建线程

1. 认识线程&#xff08;Thread&#xff09; 1.1 概念 1) 线程是什么 一个线程就是一个 "执行流". 每个线程之间都可以按照顺序执行自己的代码. 多个线程之间 "同时" 执行着多份代码. 举例&#xff1a; 还是回到我们之前的银⾏的例⼦中。之前我们主要描…...

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黑刃电动剃须刀上手

剃须刀大家都用过&#xff0c;我比较喜欢电动剃须刀&#xff0c;尤其是多刀头的悬浮剃须刀&#xff0c;感觉用起来很方便&#xff0c;剃须效率也很高。最近我在用一款DOCO小蔻的黑刃电动剃须刀&#xff0c;这款剃须刀轻巧易用&#xff0c;而且性价比超高。 相比于同类产品&…...

第15关 K8s HPA:自动水平伸缩Pod,实现弹性扩展和资源优化

------> 课程视频同步分享在今日头条和B站 大家好&#xff0c;我是博哥爱运维&#xff0c;这节课带来k8s的HPA 自动水平伸缩pod&#xff08; 视频后面有彩蛋 : ) &#xff09;。 我们知道&#xff0c;初始Pod的数量是可以设置的&#xff0c;同时业务也分流量高峰和低峰&a…...

接口测试工具(Jmeter)必学技巧

安装 使用JMeter的前提需要安装JDK&#xff0c;需要JDK1.7以上版本 目前在用的是JMeter5.2版本&#xff0c;大家可自行下载解压使用 运行 进入解压路径如E: \apache-jmeter-5.2\bin&#xff0c;双击jmeter.bat启动运行 启动后默认为英文版本&#xff0c;可通过Options – Choos…...

Day131 | 灵神 | 回溯算法 | 子集型 子集

Day131 | 灵神 | 回溯算法 | 子集型 子集 78.子集 78. 子集 - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a; 笔者写过很多次这道题了&#xff0c;不想写题解了&#xff0c;大家看灵神讲解吧 回溯算法套路①子集型回溯【基础算法精讲 14】_哔哩哔哩_bilibili 完…...

关于nvm与node.js

1 安装nvm 安装过程中手动修改 nvm的安装路径&#xff0c; 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解&#xff0c;但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后&#xff0c;通常在该文件中会出现以下配置&…...

【快手拥抱开源】通过快手团队开源的 KwaiCoder-AutoThink-preview 解锁大语言模型的潜力

引言&#xff1a; 在人工智能快速发展的浪潮中&#xff0c;快手Kwaipilot团队推出的 KwaiCoder-AutoThink-preview 具有里程碑意义——这是首个公开的AutoThink大语言模型&#xff08;LLM&#xff09;。该模型代表着该领域的重大突破&#xff0c;通过独特方式融合思考与非思考…...

USB Over IP专用硬件的5个特点

USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中&#xff0c;从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备&#xff08;如专用硬件设备&#xff09;&#xff0c;从而消除了直接物理连接的需要。USB over IP的…...

【7色560页】职场可视化逻辑图高级数据分析PPT模版

7种色调职场工作汇报PPT&#xff0c;橙蓝、黑红、红蓝、蓝橙灰、浅蓝、浅绿、深蓝七种色调模版 【7色560页】职场可视化逻辑图高级数据分析PPT模版&#xff1a;职场可视化逻辑图分析PPT模版https://pan.quark.cn/s/78aeabbd92d1...

return this;返回的是谁

一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请&#xff0c;不同级别的经理有不同的审批权限&#xff1a; // 抽象处理者&#xff1a;审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...

Python Ovito统计金刚石结构数量

大家好,我是小马老师。 本文介绍python ovito方法统计金刚石结构的方法。 Ovito Identify diamond structure命令可以识别和统计金刚石结构,但是无法直接输出结构的变化情况。 本文使用python调用ovito包的方法,可以持续统计各步的金刚石结构,具体代码如下: from ovito…...

Java求职者面试指南:计算机基础与源码原理深度解析

Java求职者面试指南&#xff1a;计算机基础与源码原理深度解析 第一轮提问&#xff1a;基础概念问题 1. 请解释什么是进程和线程的区别&#xff1f; 面试官&#xff1a;进程是程序的一次执行过程&#xff0c;是系统进行资源分配和调度的基本单位&#xff1b;而线程是进程中的…...

解决:Android studio 编译后报错\app\src\main\cpp\CMakeLists.txt‘ to exist

现象&#xff1a; android studio报错&#xff1a; [CXX1409] D:\GitLab\xxxxx\app.cxx\Debug\3f3w4y1i\arm64-v8a\android_gradle_build.json : expected buildFiles file ‘D:\GitLab\xxxxx\app\src\main\cpp\CMakeLists.txt’ to exist 解决&#xff1a; 不要动CMakeLists.…...

MySQL:分区的基本使用

目录 一、什么是分区二、有什么作用三、分类四、创建分区五、删除分区 一、什么是分区 MySQL 分区&#xff08;Partitioning&#xff09;是一种将单张表的数据逻辑上拆分成多个物理部分的技术。这些物理部分&#xff08;分区&#xff09;可以独立存储、管理和优化&#xff0c;…...