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

news2024/9/22 1:35:40
  • 首发公号:Rand_cs

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

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

一次完整的磁盘流程

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

请添加图片描述

read
    int $T_SYSCALL
        sys_read
            fileread
                readi
                    bread
                        bget
                        iderw
                            idestart

还是从 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.lock
    panic("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 进程用户态(开中断)

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

请添加图片描述

main
    userinit //准备好 initcode 进程
    mpmain
        scheduler  // 进入调度器

调度器上下文:

  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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1264641.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java 基础学习(三)循环流程控制与数组

1 循环流程控制 1.1 循环流程控制概述 1.1.1 什么是循环流程控制 当一个业务过程需要多次重复执行一个程序单元时&#xff0c;可以使用循环流程控制实现。 Java中包含3种循环结构&#xff1a; 1.2 for循环 1.2.1 for循环基础语法 for循环是最常用的循环流程控制&#xff…

WPF Live Charts2 自学笔记

文章目录 前言实现效果微软平台的历史问题 WPF 项目搭建Nuget添加额外框架添加项目初始化livecharts配置其它LiveCharts2 案例简单案例Demo示例ViewViewModel GPU渲染 Github地址仓库 前言 LiveChart 是C# 上面很受欢迎的统计图 UI控件。最近在学WPFhalcon开发&#xff0c;想想…

【.NET全栈】.net的微软API接口与.NET框架源码

文章目录 0 前言1 微软官方.net接口学习2 .NET框架源码总结 0 前言 如果浏览器打不开链接&#xff0c;换一个浏览器打开。 我是 打不开微软的链接&#xff0c;使用&#xff1a; 可以打开&#xff01;&#xff01;&#xff01; 1 微软官方.net接口学习 https://docs.microsoft…

【藏经阁一起读】(78)__《Apache Tomcat 的云原生演进》

【藏经阁一起读】&#xff08;78&#xff09; __《Apache Tomcat 的云原生演进》 目录 __《Apache Tomcat 的云原生演进》 一、读后感 二、文章知识点摘要 2.1、Tomcat的技术内幕和在喜马拉雅的实践 2.2、GraalVM static compilation in web container application&…

windows系统mobaxterm远程执行linux上ssh命令

命令如下 start "" "%~dp0\MobaXterm_Personal_23.4.exe" -newtab "sshpass -p root ssh root192.168.11.92 mkdir 33" -p 是密码 左边是用户名&#xff0c;右边是服务器ip 后面跟的是服务器上执行的命令 第一次执行的时候要设置mobaxt…

海翔云平台 getylist_login.do SQL 注入漏洞复现

0x01 产品简介 海翔云平台一站式整体解决方案提供商&#xff0c;业务涵盖 批发、连锁、零售行业ERP解决方案、wms仓储解决方案、电商、外勤、移动终端&#xff08;PDA、APP、小程序&#xff09;解决方案。 0x02 漏洞概述 海翔云平台getylist_login.do接口处存在SQL注入漏洞&am…

《数据结构、算法与应用C++语言描述》-线索二叉树的定义与C++实现

_23Threaded BinaryTree 可编译运行代码见&#xff1a;GIithub::Data-Structures-Algorithms-and-Applications/_24Threaded_BinaryTree 线索二叉树定义 在普通二叉树中&#xff0c;有很多nullptr指针被浪费了&#xff0c;可以将其利用起来。 首先我们要来看看这空指针有多少…

老师怎样预防校园欺凌的发生

作为老师&#xff0c;面对校园欺凌这个问题&#xff0c;我觉得有必要为各位老师提供一些实用的建议和策略。因为大家都知道&#xff0c;校园欺凌的存在不仅会对学生造成身心伤害&#xff0c;还会对整个教育环境产生负面影响。 关注学生的心理健康 校园欺凌往往与学生的心理问题…

手把手教你:基于python+Django的英文数据分析与可视化系统

系列文章 手把手教你&#xff1a;基于Django的新闻文本分类可视化系统&#xff08;文本分类由bert实现&#xff09;手把手教你&#xff1a;基于python的文本分类&#xff08;sklearn-决策树和随机森林实现&#xff09;手把手教你&#xff1a;基于TensorFlow的语音识别系统 目录…

Java Agent探针技术

前言 Java Agent基于字节码增强技术研发&#xff0c;支持自动埋点完成数据上报&#xff0c;Java Agent包含(并二次分发)opentelemetry-java-instrumentation CNCF的开源代码&#xff0c;遵循Apache License 2.0协议&#xff0c;在Java Agent包中对opentelemetry License进行了…

为什么国密SSL证书越来越受市场青睐

随着信息技术的迅猛发展&#xff0c;网络安全问题备受关注。在这个背景下&#xff0c;越来越多的单位纷纷选择国密SSL证书&#xff0c;以构建更为安全可靠的网络环境。那么&#xff0c;为什么这么多单位选择国密SSL证书呢&#xff1f; 1&#xff0c;国家政策支持 近年来&#…

战略制定|竞争战略管理分析六大常用工具

企业战略可从多个角度理解&#xff0c;体现为著名的5P模型。首先&#xff0c;从未来发展视角看&#xff0c;战略是一种计划(Plan)&#xff0c;指导企业朝向既定目标前进。而从过去的发展历程看&#xff0c;它呈现为一种模式(Pattern)&#xff0c;反映了企业的历史行为趋势。在产…

【Python】Selenium自动化测试框架

设计思路 本文整理归纳以往的工作中用到的东西&#xff0c;现汇总成基础测试框架提供分享。 框架采用python3 selenium3 PO yaml ddt unittest等技术编写成基础测试框架&#xff0c;能适应日常测试工作需要。 1、使用Page Object模式将页面定位和业务操作分开&#xff0…

uniapp+微信小程序监听返回事件

代码附在最后 适用场景&#xff1a;uniapp开发微信小程序 需求是我点击列表进入数据信息的详情界面&#xff0c;点击详情界面的收藏&#xff0c;返回上一界面后&#xff0c;更新列表中的收藏情况。 目录 一、使用onUnload监听页面卸载 二、使用getCurrentPages()获取当前页…

关于网站的favicon.ico图标的设置需要注意的几点

01-必须在网页的head标签中放上下面的说明&#xff1a; <link rel"shortcut icon" href"/favicon.ico">否则&#xff0c;浏览器虽然能读到图标&#xff0c;但是不会把图标显示在标签上。 02-浏览器会默认到网站根目录中读取文件favicon.ico&#x…

Java线程通信

线程通信 案例 package com.itheima.d4;public class ThreadTest {public static void main(String[] args) {Desk desk new Desk();//创建3个生产者线程new Thread(() -> {while (true) {desk.put();}}, "厨师1").start();new Thread(() -> {while (true) {…

MySQL数据库入门到大牛_基础_18_MySQL8其它新特性(MySQL基础部分最后一章;新特性概述;窗口函数;公用表表达式)

文章目录 1. MySQL8新特性1.1 MySQL8.0 新增特性1.2 MySQL8.0移除的旧特性 2. 新特性1&#xff1a;窗口函数2.1 使用窗口函数前后对比2.2 窗口函数分类2.3 语法结构2.4 分类讲解1. 序号函数2. 分布函数3. 前后函数4. 首尾函数5. 其他函数 2.5 小 结 3. 新特性2&#xff1a;公用…

vsphere系列 :虚拟机配置直通GPU后,启动时出现 模块“DevicePowerOn”打开电源失败 的解决方案

vsphere中的虚拟机配置直通GPU后&#xff0c;启动时出现 模块“DevicePowerOn”打开电源失败 的解决方案 vsphere中的虚拟机配置直通GPU后&#xff0c;启动时出现 模块“DevicePowerOn”打开电源失败 的解决方案1、虚拟机配置GPU直通1、打开虚拟机选项2、点击编辑配置3、添加如…

SpringBoot+VUE3前后端分离-【支付宝支付】

1、支付宝沙箱应用申请 https://open.alipay.com/develop/sandbox/app 打开支付宝沙箱能够看到如下信息&#xff1a; 获取到appid&#xff1b; 2、获取应用私钥以及支付宝公钥 在接口加密方式选择公钥模式启用&#xff0c;根据操作即可获取应用公钥、应用私钥以及支付宝公钥…

别再让假的fiddler教程毒害你了,来看这套最全最新的fiddler全工具讲解

fiddler界面工具栏介绍 添加图片注释&#xff0c;不超过 140 字&#xff08;可选&#xff09; &#xff08;1&#xff09;WinConfig&#xff1a;windows 使用了一种叫做“AppContainer”的隔离技术&#xff0c;使得一些流量无法正常捕获&#xff0c;在 fiddler中点击 WinConfig…