计组强化大题

概览

  • 数据的表示和运算计算量比较大,其他的计算量较小
  • 存储系统是计组最重要的章节

  • 一条指令的执行过程是第五章CPU的内容
  • 指令序列的工作过程是指令序列的内容
  • 第四章第五章中间可能还会穿插着指令流水线
  • 计组一定要吃透历年真题

存储系统串讲

地址变换过程

框架

  • 程序主要由一系列指令和数据组成,指令控制着程序运行的逻辑,而数据是指令运行过程中需要的一些内容

    • 比如手机游戏如果遇到一个npc,那么手机就需要加载这个npc的数据资源,需要从手机的外存里面,对应的数据当中读取出这个npc的模型
    • 这个3d模型会从外存中被加载到主存(内存),然后cpu会把内存当中这个3d模型的数据进行渲染,在cpu进行计算后会直到这个模型在显示器上应该显示成什么样子,最终cpu计算完之后会转换成像素的信息,然后显示在手机上
  • 安装的这个APP可能有3GB,指令可能占了1GB,数据占了2GB,这些都是安装在外存里面的

    • 当启动APP以后,数据被加载到内存里面

    • 操作系统先会建立这个APP的进程,这个进程的大小取决于操作系统的位数

      • 比如说操作系统是32位的,那么它的一个进程,虚拟地址空间/逻辑地址(VA)总共占32位,2^32=4G,每个地址对应一个字节,所以32位的操作系统启动一个进程,这个进程的大小就是4GB
      • 所以虽然这个APP本身只有3GB的大小,但是当它启动在一个32位的操作系统之上的时候,它总共会有4GB的虚拟地址空间
      • 但我们不需要把全部的指令和数据都加载到内存当中,这个4GB是最大的虚拟地址空间的大小,也就是APP进程启动后,最大会占4GB的内存,但是事实上进程一般来说不用占那么多的地址空间,实际占个1GB就差不多了,数据和指令在需要的时候调入
        • 比如我在地图一进行游玩,不需要把地图二地图三的数据调入,等到我进入地图二再把地图二调入,启动游戏时候的loading,正在加载资源或者游戏场景,就是把相关的数据和指令从外存调入内存的的过程
        • 当我们在野外遇到一个全新的npc的时候,这个npc的模型资源还没有调入内存,但我们又需要使用这个npc的资源的时候,就会发生缺页(实际反馈就是卡顿)
        • npc的资源保存在外存里面,需要加载但是还没有调入内存就发生了缺页,那就会进入缺页异常的处理逻辑,就会调页,调页之后重新刷新,加载,渲染到屏幕上
      • 对于一个32位的虚拟地址空间来说,并不是全部都要用到,很多部分都是空闲的,有需要才会使用

  • 虚拟地址空间的1GB放到物理内存里面

    • 这1GB的数据和数据会被拆分成页,离散地放到内存当中的各个位置,只要确定一个页的大小,就可以确定1GB的指令和数据会被拆分成多少个页
  • 虚拟地址(VA)到物理地址(PA)的转换,需要查页表,查页表才知道虚拟地址对应的物理地址

    • 操作系统管理一个数据结构叫页表,操作系统会管理这个进程的页表,而页表指明了这个进程的某一个虚拟地址和物理地址之间的对应关系
  • 计算机的本质就是执行程序,而执行程序的过程无非就是不断地重复取指令和执行指令,取指令的指令就放在程序计数器(PC)当中

    • 程序计数器存储的CPU要执行的下一条指令的虚拟地址
    • 当APP启动的时候,首先会把某一些即将执行的指令,还有某一些即将用到的数据,给调入到内存当中,同时,操作系统会给这个进程建立一个4GB的虚拟地址空间,这个APP启动的时候要执行的第一条指令的虚拟地址,就直接放到CPU的PC计数器里面,然后去找到接下来要执行的那条指令
      • 指令访问的过程中,可能会访问到某一些数据
        • 参考指令的寻址方式
        • 指令需要的数据可能来自于寄存器,也可能是立即数,也有可能来自于内存
    • 如果这条指令访问的数据存储在内存中,这个数据需要去内存中去找,但是这个指令内部,只知道这个数据存储在哪个虚拟地址当中(进程的第x个字节),因此当指令要访问这个数据的时候,也需要把这个虚拟地址转换成物理地址,也就是需要去查页表,最终得到一个物理地址,在这个物理地址当中的某一个页里面找到这个数据的实际内容
  • 访问主存或者说访问某一个虚拟地址的两个主要事件

    • 取指令的时候根据PC所指的虚拟地址,取出指令的时候,需要把它转换成物理地址,再从内存当中取出该条指令
    • 在执行指令的过程中,需要的数据可能在主存里,指令内部只知道数据存储的虚拟地址,需要把虚拟地址转换成物理地址,然后再去主存当中找到这个指令要处理的数据,然后从内存取到CPU,然后CPU再根据指令的功能对数据进行处理
    • 因此无论是取指令还是执行指令过程中,都可能涉及虚拟地址转换成物理地址的一个过程
  • CPU从哪里直到这个数据的虚拟地址

    • 指令的地址码
      • 指令由操作码字段和地址码字段构成
        • 操作码指明了指令的功能
        • 地址码则指明了这条指令要处理的数据存储在什么地方
    • 下一条执行的指令存储在PC中,然后它会被取到IR指令寄存器当中(正在执行的指令),通过IR,CPU可以清楚的知道指令的操作码和地址码是什么,如果地址码字段的寻址方式指出数据在内存当中,那么地址码就会指出数据的虚拟地址VA
    • 然后通过查页表将VA转换成PA,然后再从内存里找到指令所需要的数据

地址转换

  • 虚拟地址的位数就是虚拟地址的位数
  • Cache是CPU内部的高速存储器,不属于操作系统的范畴,是硬件自动实现的内容,不是操作系统去实现的内容
  • MAR里面存储的是物理地址
    • 只有把虚拟地址转换成物理地址之后,最终准备好要访存的时候,才会被写进MAR里面
  • 虚拟地址到物理地址的转换需要经过一个部件,MMU(内存管理单元),经过MMU的处理,它会把VA转换为PA,然后PA会放进MAR寄存器(逻辑上也从属于MMU)里面
  • 页表存储在内存里面,但因为内存查询起来比较慢,所以内存里面的页表被称为慢表
    • 而为了让这个页表查询起来更快,TLB快表存储在CPU内(逻辑上属于MMU),硬件实现,在CPU内部就可以实现虚拟地址和物理地址的转换
    • 根据虚拟地址去查TLB,如果快表命中,可以直接得到物理地址,不用去访问内存里面的慢表
  • CPU如何判断当前正在执行的这个进程,页表在内存中的存储位置?
    • 页表始址寄存器(逻辑上属于MMU),指明了当前正在运行的这个进程的页表始址,它的页表在内存里存储在什么地方,直接指明页表在内存当中起始的物理地址
      • 只要知道起始地址,同时知道虚拟地址的页号,也知道页表项的长度,结合起来就能得到页号对应的页表项,然后就可以把这个页表项加载到TLB当中
      • 查到页表项之后,既知道了我的这个虚拟地址对应的物理页框号等于多少,同时我也会把这个页表项的内容复制一份到TLB当中,下一次想要访问同一个页框或者同一个页面的时候,就可以直接从TLB命中
      • 页框是物理内存中划分出来的固定大小的存储区域
      • 页面是逻辑地址空间划分出来的固定大小的单元,页框和页面大小相等
  • CPU的页表始址寄存器里面的值从哪里来的?
    • 来自于PCB进程控制块
      • 操作系统会给每个进程建立一个数据结构叫做进程控制块PCB
        • 而这个进程控制块里面就有一个变量记录了这个进程的页表始址
      • 当一个进程被调度的时候,这个进程即将上处理机运行的时候,操作系统会把PCB里面的这个页表始址写到CPU的页表始址寄存器里面
        • 这样的话,当这个进程正在运行的时候,CPU可以直接根据页表始址寄存器里的值,直接找到页表的存储位置
        • 页表始址寄存器里面保存的是物理地址
      • 如果再换一个进程运行,那么我再从另一个PCB当中找到另一个进程的页表始址寄存器,页表始址再把这个值覆盖

  • 无论是查快表还是慢表,最终要得到的就是物理页框号
  • 物理页框号拼接页内偏移量得到完整的物理地址存储在MAR里面

地址转换:查一级页表

  • 每一个页表项多少个字节,由操作系统和CPU决定,通常和CPU有关,只要确定了CPU的型号,基本上就确定了一个页表项
  • 页号是隐含的,只需要确保页表项在内存中连续存储
  • 首先要检查页表的有效位,有效位为1意味着页面此时是在内存里面的,没有缺页,有效位为0,则意味着缺页,缺页的时候需要从外存把页面的数据调进来
    • 页表项中会保存这个页面在外存当中的地址,如果有效位为0,发生缺页,操作系统就可以根据这个信息去外存当中找到缺失的页面,然后调入内存,更新这个页表项
  • 一个进程的页表在内存当中必须是物理上连续存放的,虽然这个页表可能很长,但是为了支持根据页表起始地址和页号找到目标页表项,必须保证这些页表项在物理上是连续的

地址转换:全相联TLB命中+不用查页表

  • 快表为什么很快?
    • 采用比较器(异或门元器件)
      • 当且仅当每一bit都相同的时候结果全0,结果全部取非然后相与,只有所有的位都是1,结果才会是1,结果为1说明两者完全一致
      • 如果有一个位不一致,则结果存在一个1,然后取非出现一个0,最后相与结果必为0,所以不一致
  • 每一个TLB表项对应的就是一个TLB寄存器,虚拟地址和TLB的比较,就是比较器电路全部连接到每一个TLB寄存器的标记位,只有虚拟地址的页框号和TLB的标记能够完全匹配的时候,这一行TLB才会被选中
  • 选中的逻辑电路设计
    • 匹配成功得到信号1(比较器产生的信号),这个信号可以控制一个三态门,只有匹配的TLB寄存器才能被选通
    • 标记为和有效位再进行一个相与,然后把这个相与的信号作为刚才提到的这个TLB寄存器的三态门的选通信号,这就可以实现TLB的迅速并行比较,迅速获得想要的页框号
  • 查页表的时候有可能发生有效位为0的情况
    • 有效位为0,说明这个页表一定是缺页的
  • 如果快表命中,快表的有效位等于1,标记位也可以匹配得上,不可能缺页
    • 页面置换的时候,由一些页面可能被换到外存,换到外存之后,这个页面的有效位就从1修改为0,同时记录这个页面在外存当中存储的地址
      • 操作系统同时也会通知CPU让他把这个页表项的有效位置为0,也就是说当页面换出的时候,TLB的表项肯定也会跟着一起失效
    • 因此只要TLB命中一定不会缺页,TLB不命中查页表的时候,才有可能缺页

地址转换:全相联TLB未命中+查一级页表

  • 查慢表
    • 根据页表始址寄存器去找页表的起始地址
    • 根据每一个页表项的大小,结合页号,然后在内存当中找到要的页表项,也后确定它的页框,把他的页框放到MAR寄存器前页框号位,拼接页内偏移量得到物理地址

地址转换:查二级页表

  • n级页表下虚拟地址的页框号被拆分为n段
  • 以当前2级页表为例,高12位被用于查一级页表,CPU内部会记录第一级页表的起始地址,根据一级页号找到对应的页表项
    • 假设二级页表有效位为1,说明二级页表没有缺页,已经调入了内存,那么就可以知道二级页表在内存的哪个位置(这里的页框号对应的是二级页表在内存当中的起始物理地址)
      • 比如这张图中的11111H页框号代表二级页表存储在11111H这个页框里面,确认的是二级页表的起始存储地址
    • 知道二级页表的起始地址,再结合二级页表的这个页号,找到对应的页表项,然后有效位等于1代表没有缺页,找到页框号后把页框号读到MAR的高20位,再把低12位直接从这个虚拟地址那边拿过来
  • 但是,当查到二级页表的页框号的时候,保存在TLB的时候,应该保存完整的虚拟页号985211H,而不是单纯的211H
  • 无论是几级页表,虽然当我们在查慢表的时候会一级一级拆分页号进行查询,但只要我们命中了某一个页表项,并把这个页表项存储到TLB当中的时候,我们需要保存这个页表项对应的完整的所有的每一级页号,而不是只保存最后一级
    • CPU内部查TLB的时候,把整个虚拟页号作为一个整体,没有分级,作为一个整体去和全相联的表项对比

地址转换:全相联TLB未命中+查二级页表

地址转换:全相联TLB命中+不用查页表

地址转换:4路组相联TLB命中+不用查页表

  • 对TLB的表项进行分组,x路组相联就是x个页表项一组
  • 对虚拟页号进行拆分,如果有2^n个TLB组,则组号占n位,在地址低位,剩余的地址高位是TLB标记
    • 如果采用全相联映射,则TLB标记页虚拟页号完全已知,如果是组相联,则是排除组号的地址高位

访问物理地址:全相联Cache命中+不用访问主存

  • 得到了物理地址说明页面一定已经在内存里了
    • 如果页面不在内存里会发生缺页,并且调页,然后再进行一次地址转换
  • 如果没有Cache,直接根据这个物理地址,直接根据MAR指明的地址,直接去访问内存,读出这个地址对应的数据
  • 根据Cache的映射方式的不同,要对物理地址进行不同的拆分
    • 全相联映射
      • 没有行号也没有组号,直接Cache标记和每一个Cache行的标记进行对比
      • Cache块的大小转为2^nB,然后n就是字块内地址所占的位数也就是最后n位,剩余的高位就是Cache块的标记
      • 用Cache标记去和Cache里面的Cache行进行比较,如果能完全匹配并且有效位为1
    • 直接相联映射
      • 有行号
      • 相当于1路组相联映射
      • 拆分成三个结构
        • 字块内地址,同上
        • 行号,看直接映射的情况下整个Cache有多少行,然后转化为2^n行,n就是行号所占的地址数量,位于中间
        • Cache标记,除了行号和字块内地址以外的最高位
    • 组相联映射
      • 有组号
  • 访问到的数据会存储到MDR当中,然后MDR再把这个数据给到CPU,如果是指令,会放到IR指令寄存器当中,如果是数据,则可能放到CPU的某个通用寄存器当中
  • Cache块的大小和页的大小不一样的
    • 408里面一个内存块的大小等于一个Cache块的大小
    • Cache块的大小一般来说要远远小于一个页的大小

访问物理地址:全相联Cache未命中+访问主存

  • Cache标记位匹配不上和匹配上了但是有效位为0都是Cache未命中的情况

访问物理地址:直接映射Cache命中+不用访问主存

  • 直接相联映射
    • 有行号
    • 拆分成三个结构
      • 字块内地址,同上
      • 行号,看直接映射的情况下整个Cache有多少行,然后转化为2^n行,n就是行号所占的地址数量,位于中间
      • Cache标记,除了行号和字块内地址以外的最高位
    • 根据行号找到对应的行,行号匹配以后去对比Cache标记,如果Cache标记匹配+有效位为1,那么Cache命中,可以直接从Cache块中根据字块内地址读取数据,读到MDR当中

访问物理地址:直接映射Cache未命中+访问主存

访问物理地址:2路组相联Cache命中+不用访问主存

  • 组相联映射
    • 有组号
    • x路组相联映射的意思是每组有X个Cache行
    • Cache标记+组号+字块内地址
      • 字块内地址同上
      • 组号是一共有2^n组,组号占n位
      • 剩余的高位是Cache标记
  • 根据组号找到对应的分组,然后对比组内各Cache行的Cache标记和地址的Cache标记,匹配上以后对比有效位
  • Cache行和Cache块不是一个东西
    • Cache行包括了Cache的标记位,有效位,替换位等辅助控制信息,再加上Cache块的数据,而Cache块指的就是主存数据的副本,也就是64B本身

访问物理地址:2路组相联Cache未命中+访问主存

三种映射方式的电路图

  • 图中中间的8个bit对应组号
    • 组号连接的折线表示8个bit可以命中某一个分组,这一个分组当中包含了4个Cache行
  • 高22bit对应的是Cache的标记位
    • 标记对应的折线,最下面连接了4个比较器(⭕+=)(用异或门,非门实现的东西)
    • 每一个Cache行当中都有tag标记位,每一个Cache行都会把这22个标记位和刚才我们要访问的这个物理地址的tag信息进行对比
    • v是有效位,有效位和TLBCache行标记位和地址标记位比较的结果输入与门的两端进行相与,产生的结果输入到最末端的四路选择器(多路选择器)
    • 四个Cache块的数据直接连到多路选择器当中,当上述步骤产生了1信号以后,会把对应Cache块的信号选通,让其通过多路选择器,然后就得到了一整个Cache块
    • 这个Cache块结合字块内地址,就可以找到想要的数据

存储系统真题

Cache的题目如何分析

  • 考法
    • 虚拟地址转化为物理地址的过程
      • 涉及TLB查快表
      • 查慢表
    • 通过物理地址去访问Cache的过程
      • 静态分析
        • Cache的地址结构(PA拆分)
        • Cache行的完整构成
          • TAG标记位
          • 有效位
          • 脏位
          • LRU替换位
          • 数据位(Cache块)
      • 动态分析
        • 物理地址访问序列
          • Cache命中率
            • 命中的地址,不命中的地址
          • Cache的替换
          • 直接给物理地址序列
          • 数组元素访问(for循环)
            • 把对数组元素的访问,转化成对物理地址的访问
          • 以机器代码形式
            • 哪些时候取指令,哪些时候访问数组数据

第一类题型:分析Cache的地址结构

  • 类型
    • 取决于映射方式
    • 直接映射
      • 三个部分
      • TAG标记+行号+块内地址
    • 组相联映射
      • 三个部分
      • TAG标记+组号+块内地址
    • 全相联映射
      • 两个部分
      • TAG标记+块内地址
  • 读题的时候需要关注的条件
    • 物理地址的位数(机器字长)
    • Cache总行数
      • =数据区大小/Cache块大小
      • Cache数据区总大小是指Cache数据块大小之和,只包括数据部分
      • Cache总容量(总大小)≠Cache数据区总容量
      • 组数=Cache总行数/组相联路数
    • Cache块大小
      • 一般直接给出
    • 映射方式
      • 直接映射
      • 组相联
        • 关注路数,确定组数
      • 全相联
    • 先关注PA位数,再关注映射方式,接着关注Cache总行数和块大小,从后往前推,先确定块内地址,再确定行号和组号,最后确定TAG标记
第二类题型:Cache行的完整构成

  • 读题时需要关注的条件

    • 物理地址位数
    • Cache总行数
    • Cache块大小
    • 映射方式
    • 写策略
      • 直写法
        • 修改Cache的同时页同步修改主存里的数据
        • 不需要脏位
      • 回写法
        • 先修改Cache里面的副本数据,暂时不会修改主存里的数据,只有当数据块从Cache里面淘汰调回主存的时候,才把整个数据块写回到主存
        • 需要1bit脏位
    • 替换算法
      • 随机算法
        • 不需要替换位
      • 其他
        • 需要替换位,位数取决于替换的范围,2^n个中替换,需要nbit替换位
          • 组相联=log2(路数)
          • 直接映射不需要替换位
        • 先进先出算法(FIFO)
        • 近期最少使用算法(LRU)
        • 最不经常使用算法(LFU)
  • 构成

    • TAG
      • Cache地址高标记位
    • 有效位
      • 一定存在
      • 占1bit
      • 如果发生进程切换,有效位会发生巨大的变化,意味着Cache里面这些主存的副本数据是上一个进程的数据,当我们切换一个进程的时候,这个新进程的数据副本和上一个进程肯定是不一样的,因此切换进程的时候需要把Cache的所有有效位置为0
        • 一个软件刚开始的时候比较卡也是因为Cache最开始有效位全位0,Cache当中还没有填充进来,最开始一定会出现Cache缺失
    • 块内数据
      • 根据Cache块大小确定
    • 脏位
      • 写回法1bit
      • 直写法0bit
    • 替换信息位
      • 随机替换0bit
      • 其他 FIFO,LRU取决于范围
        • 直接映射0bit
        • 组相联log2路数bit
        • 全相联log2行数bit
          • 行数=Cache数据区总大小/Cache大小
    • 组号和行号实际上是隐含的,不需要占任何的bit

Cache大题实战

2020

  • 思路
    • 主存地址32位→PA有32位
    • 8路组相联映射方式
    • 直写法
    • LRU替换算法
    • 主存块大小64B
    • 数据区容量32KB
      • 由主存块和数据区容量可以推出Cache总行数
    • 第一小问:Cache行的完整构成
    • 第二小问:动态分析PA访问序列,Cache命中率
      • 变量k分配在寄存器里面
        • 不用考虑变量k的访存,不涉及Cache的访问
      • 起始地址+数组
        • 告诉即将要访问的地址是哪几个
      • 主存块大小
        • 除了末尾的6bit之外,高位相同意味着这几个地址都从属于同一个Cache块
          • 第一次访问存在Cache缺失,之后Cache块被调入,之后访问同一块数据不会缺失
    • 第三小问:CPU访问Cache的操作
      • 根据映射方式拆分PA
      • 如果是直接映射,找到对应行号,组相联找到对应组号
      • 对比TAG标记位
      • 匹配上后检查有效位
      • 如果上述都匹配,命中
      • 缺失的话会把对应的主存块读到Cache里面,修改Cache行内部的tag标记有效位和LRU替换算法位,还有脏位
2010

  • 条件
    • 主存地址空间大小256MB
      • 28bit
    • 8个Cache行,每个Cache行大小64B
      • 行号3bit,块内地址6bitt
    • 直接映射方式
    • int32位补码表示
      • 4B
    • i、j、sum分配在寄存器中
      • 三个变量不会涉及Cache访存
    • 数组a按行优先方式存放
      • 行遍历顺序
    • 首地址320
  • 思路
    • 第一小问:Cache的总容量
    • 第二小问:访问的地址
      • 十进制转二进制,然后取出对应的位数
    • 第三小问:访问命中率
2018

  • 条件
    • VA=20+12
    • PA=20+3+5
    • 全相联TLB
    • 二级页表
  • 思路
    • VA→PA→Cache
    • PA分成三个部分→要么是组相联,要么是直接映射
    • 一个组号指向两个Cache行→组相联映射
2023

TLB的题目如何分析

  • 关注
    • VA多少位,虚页号多少位?
      • 页号+页内地址
    • TLB的映射方式
      • 全相联
      • 组相联映射
        • k路
      • 直接相联很容易出现抖动现象,效率低,一般不出现
    • 替换算法
      • 同Cache
      • 可能出现替换信息位
  • 所有涉及到TLB的题目,一定要关注虚页号如何拆分的问题
  • 每个TLB行(表项)完整构成:
    • TAG
      • 组相联
        • 虚页号除了末尾log2(组数)的最高位
      • 全相联
        • 虚页号
    • 有效位
      • 一定存在1bit
    • 页框号
      • 看页框数量(物理页框,要拼接在页内偏移量前面形成PA的)
    • 替换信息位
      • 看TLB替换算法
        • 随机
          • 0bit
        • 其他
          • 组相联
            • log2(路数)
          • 全相联
            • log2(总行数)

TLB大题实战

  • 条件
    • VA30bit
    • 页大小4KB→12bit页内偏移量
    • 总共八组→组号3bit

数据的表示和运算

  • 数据的表示和运算往往还会和指令的运行过程结合起来考察

大纲

  • 数制与编码

    • 进位计数制及其相互转换
      • 二进制、八进制、十进制、十六进制之间的转换
      • 二、八、十六之间的转换很简单
        • 八→3个bit,十六→4个bit
      • 二转十
        • 根据每位权值展开即可
      • 十转二
        • 好拼凑的
          • 拼凑法
        • 不好拼凑的
          • 整数部分
            • 除基取余法
              • 结果从后往前
          • 小数部分
            • 乘基取整法
              • 结果从前往后
    • 定点数的编码表示
      • 无符号数的表示
        • 重点关注无符号数的“减法“如何实现,以及无符号数加法/减法的溢出判断
          • 无符号数的加法直接加
          • 减法被减数不变,减数按位取反+1
            • A-B都是转为加法,A+(B取反末位+1)
          • CF标志位
      • 带符号整数的表示
          • 重点关注补码的”减法“如何实现,以及补码加法/减法的溢出判断
            • A-B都是转为加法,A+(B取反末位+1)
          • 注意偏移量是多少
      • 定点小数
        • 仅用于浮点数尾数,关注加减法即可
  • 运算方法和运算电路

    • 基本运算部件

      • 加法器

        • 一位全加器

        • 串行进位加法器

        • 并行进位加法器

        • 带标志加法器

      • 算术逻辑部件(ALU)

    • 加减运算

      • 补码加/减运算器

      • 标志位的生成

        • CF
        • SF
        • ZF
        • OF
    • 乘除运算

      • 乘除运算的基本原理
      • 乘除电路和除法电路的基本结构
  • 整数的表示和运算

    • 无符号整数的表示和运算
      • 表示
      • 运算
        • 加、减、乘、除
        • 溢出判断
    • 带符号整数的表示和运算
      • 表示
        • 原、反、补、移
        • 除了浮点数的尾数之外,所有的带符号数肯定都是用补码来表示的
      • 运算(仅关注补码即可)
        • 加减乘除
        • 溢出判断
  • 浮点数的表示和运算

    • 浮点数的表示:IEEE754标准(只需要掌握该标准,非IEEE754标准的可以不管了)
      • 结构:符号位+阶码+尾数
      • float:1+8+23
      • double:1+11+52
      • long double :1+15+64
    • 浮点数的加减运算
      • 对阶:小阶向大阶看齐
      • 尾数加减:原码定点数的加减法
        • 机器级别的实现上最终还是会变成补码的加减运算
      • 规格化:位数规格化为1.xxxx的形式
        • 尾数每左规一位,阶码-1
        • 尾数每右规一位,阶码+1
      • 舍入:尾数右规时,可能导致精度丢失,需考虑舍入问题
      • 判溢出
        • 尾数右规时,阶码超出可表示的最大值,上溢——发生溢出异常
          • 如果浮点数的加减运算有溢出的话,OF标志位会等于1
        • 尾数左规时,阶码低于可表示的最小值,下溢——当做机器0处理

考法

  • 有符号数和无符号数,长数和短数,整数和浮点数的类型转换
  • 结合C语言程序,分析”数据的表示和运算“
    • 常规考法
  • 结合机器级代码(通常也会给出对应的C语言程序),分析”数据的标识和运算“在指令运行过程中的作用
    • 加减运算在”条件转移类指令”中的运用

大题重点关注

数据的表示

  • C语言常见整数类型
    • 无符号整数
      • unsigned short
      • unsigned int
      • unsigned后面没有跟int还是short就默认int
    • 带符号整数
      • short
      • int
    • 记住16位表示范围
      • 2^16=65536
      • 2^15=32768
      • 带符号short→-32768~32767
      • 无符号unsigned short→0~65535
      • 加减溢出的快速判断
        • 十进制算结果超出表示范围一定溢出
  • IEEE754浮点数
    • 结构:符号位+阶码+尾数
      • float:1+8+23
      • double:1+11+52
      • long double:1+15+64
  • C语言数据类型转换
    • 整数之间的转换
      • 长短相同
        • 机器数不变,解释方式改变(带符号整数、无符号整数)
      • 短变长
        • 先扩展(无符号整数零扩展、带符号整数符号扩展),再解释
      • 长变短
        • 先截断(留低位),再解释
    • 整数和浮点数之间的转换
      • 整数转浮点数
        • 写出人类视角的科学计数法1.xxxx,再转为浮点数,截断尾部时采用0舍1入原则
        • 注意:精度丢失问题
      • 浮点数转整数
        • 写出二进制小数,去掉小数部分,整数部分保留更低的位数
        • 注意:溢出问题、精度丢失(小数点后的部分)

数据的运算

  • 加减法

    • 无符号整数
      • 加法规则
        • 机算
          • 按位相加
        • 手算
          • 代入十进制机算
      • 减法规则
        • 机算
          • 减数按位取反末位加1,减法变加法
        • 手算
          • 代入十进制计算
      • 溢出问题
        • 手算
          • 判断加减法的结果是否超出无符号数合法范围
        • 机算
          • 根据CF标志位判断,CF=最高位产生的进位 ⊕ SUB(减1加0)
    • 带符号整数
      • 加法规则
        • 机算
          • 按位相加
        • 手算
          • 代入十进制计算
      • 减法规则
        • 机算
          • 减数按位取反末尾加一,减法变加法
        • 手算
          • 代入十进制计算
      • 溢出问题
        • 手算
          • 判断加减法的结果是否超出带符号数合法范围
        • 机算
          • 根据OF标志位判断,OF=最高位产生的进位 ⊕ 次高位产生的进位
    • IEEE754浮点数
      • 小题考察居多
    • 什么时候用手算,什么时候用计算
      • 优先手算,溢出的时候机算
  • 乘法

    • 无符号整数
      • 机算
        • 注意:可用逻辑左移代替*2
          • 逻辑左移,低位补0,高位直接移出去,逻辑右移,低位直接移出去,高位补0
      • 溢出判断
        • n位*n位,若用2n位保存乘积,则不会溢出
        • n位*n位,若用2n位保存中间结果,最后截取末尾n位作为最终的乘积,可能会溢出
          • 手算判溢出
            • 代入十进制计算乘法结果,判断该结果是否超出了n位“无符号数”所能表示的范围,若超出,则溢出
          • 机器判溢出
            • 两个无符号数乘法:n位*n位,用2n位保存中间结果,仅当前n位都是0时,不溢出
    • 带符号整数
      • 机算
        • 注意:可用算术左移代替*2
          • 算术左移,低位补0,高位直接移出去,算术右移,低位丢弃,高位补符
      • 溢出判断
        • n位*n位,若用2n位保存乘积,则不会溢出
        • n位*n位,若用2n位保存中间结果,最后截取末尾n位作为最终的乘积,可能会溢出
          • 手算判溢出
            • 代入十进制计算乘法结果,判断该结果是否超出了n位“无符号数”所能表示的范围,若超出,则溢出
          • 机器判溢出
            • 两个有符号补码乘法:n位*n位,用2n位保存中间结果。仅当前n+1位全0或者全1时,不溢出
  • 除法

    • 无符号整数
      • 机算
        • 可用逻辑右移替代/2
    • 带符号整数
      • 机算
        • 可用算术右移代替/2
    • 408考试中除法不可能溢出,只丢失精度

常见的难点

无符号数的加法、减法怎么算

  • 在硬件看来,对无符号数的加减法、对有符号补码的加减法都是一样的计算方法

    • 共用同一套电路
  • 加法:直接N bit二进制按位相加

  • 减法:计算”A-B“可转化为等价的加法——将B的N bit全部按位取反末位+1,然后与A相加

遇到乘法怎么算

  • 通常可代入十进制手算
  • 408大纲不要求掌握浮点数乘法

遇到除法怎么算

  • 通常可代入十进制手算
  • 408大纲不要求掌握浮点数除法

溢出怎么判断

  • 加法/减法
    • 手算判溢出
      • 代入十进制计算结果,判断该结果是否超出了n位所能表示的范围,若超出,则溢出
    • 机器判溢出
      • 无符号数加/减:运算后若CF=1,则溢出;若CF=0,则不溢出
  • 乘法
    • n位*n位,若用2n位保存乘积,则不会溢出
    • n位*n位,用2n位保存中间结果,最后截取末尾n位作为最终的乘积,可能会溢出
      • 手算判溢出
        • 代入十进制计算乘法结果,判断该结果是否超出了n位所能表示的范围,若超出,则溢出
      • 机器判溢出
        • 两个无符号数乘法:n位*n位,用2n位保存中间结果。仅当前n位都是0时,才不溢出
        • 两个有符号补码乘法:n位*n位,用2n位保存中间结果。仅当前n+1位全1或全0时,不溢出

精度丢失怎么判断

  • 浮点数的精度:看尾数的位数
    • 单精度浮点型float
      • 尾数位数位23+1bit。因此数值部分可以表示24bit的精度
    • 双精度浮点型double
      • 尾数位数位52+1bit。因此数值部分可表示53bit的精度
    • 临时浮点数long double
      • 尾数位数位64+1bit。因此数值部分可表示65bit的精度
    • 注:IEEE754标准中,尾数是规格化的,隐含了一个最高位的1
  • 定点整数的精度:看数值位的位数
    • 16位short型
      • 数值部分的精度为15bit
    • 32位int型
      • 数值部分的精度为31bit
    • 32位unsigned int型
      • 数值部分精度为32bit
    • 64位long型
      • 数值部分的精度为63bit
  • 整数转浮点数
    • 数值部分精度更高的类型,转为数值部分精度更低的类型,就有可能发生精度丢失
    • 如果整数的数值位的位数等于或者小于浮点数尾数位数,精度就不会丢失,如果整数的数值位的位数大于浮点数尾数位数,精度就会丢失
    • 不会溢出
  • 浮点数转整数
    • 如果浮点数有小数部分,则可能丢失精度

标志位怎么区分

  • CF
    • 含义:进位/借位标志,表示无符号数的加减法是否发生了进位或借位。当CF=1时,说明无符号数的加减运算发生了进位或借位,也即发生了溢出
    • 硬件的计算方法
      • CF=最高位产生的进位 ⊕ SUB
        • sub=1表示减法
        • sub=1表示加法
    • 注意:CF位对有符号数的加减法无意义
  • ZF
    • 含义
      • 表示运算的结果是否为0。ZF=1表示运算结果为0,ZF=0表示运算结果非0
    • 硬件的计算方法
      • 两个数的运算结果为n bit,只有n bit全为0时,ZF=1
  • OF
    • 含义
      • 有符号数的加减运算是否发生了溢出。OF=1时,说明发生了溢出
    • 硬件的计算方法
      • OF=最高位产生的进位⊕次高位产生的进位
    • 注意:OF位对无符号数的加减法无意义
  • SF
    • 含义
      • 有符号数加减运算结果的正负性,SF=0表示运算结果为正数,SF=1表示运算结果为负数
    • 硬件的计算方法
      • SF=最高位的本位和
    • 注意
      • SF位对无符号数的加减法无意义

指令的执行

  • 第四章(指令系统)是一堆指令的执行
    • 更加宏观,不同指令之间如何配合工作,因为程序是由指令序列来完成
    • 给一个C语言程序,然后给对应的机器语言指令或汇编语言指令,去分析这些指令序列的工作过程
  • 第五章(中央处理器)是一条指令的执行
    • 更为微观,一般来说结合硬件电路去考察,一条指令使用硬件是如何实现的,如何实现这一条指令的功能

大纲

  • 指令系统的基本概念
  • 指令格式
    • 指令的基本格式

      • 操作码

        • 指明了动作
      • 操作数(可能有0~n个)

        • 来源
          • 寄存器
          • 主存
          • 立即数
        • 寻址方式
          • 怎么找到这个操作数
        • 形式地址
          • 形式地址(需转成有效地址EA)
          • 指令运行时需转成有效地址EA
      • 根据指令长度是否固定不变,分为两类

        • 定长指令字

          • 每次PC+“1”,“1”固定
        • 变长指令字

          • 每次PC+“1”,“1”不固定
        • 十六进制加减快速运算

          • 加法转化为十进制,然后逢16进1,拆分为16+x,x就是剩下的位数
          • 减法同理,不够减就借位,然后加上16再减,参考十进制

    • 定长操作码指令格式

      • 操作码的位数固定不变
    • 扩展操作码指令格式

      • 操作码的位数可能会改变
  • 寻址方式
    • 有效地址的概念
      • EA:最终要访问的地址
    • 要寻找什么?
      • 数据寻址
        • 找数据——找到你要操作的数据
      • 指令寻址
        • 找下一条要执行的指令——取决于PC值
        • 肯定是根据PC值去访问主存,从主存当中找到下一条要执行的指令
    • 常见的寻址方式

  • 需要关注的寻址
    • 立即寻址
    • 直接寻址
    • 寄存器寻址
    • 寄存器间接寻址
    • 相对寻址(PC+偏移量)
      • 要修改PC值的基本都是这种寻址方式,尤其是转移类指令
    • 变址寻址
      • 数组问题(起始地址+偏移量)
  • 隐含寻址:一条指令的操作数由硬件直接找到,并没有在指令中明确指出操作数在哪个地方
    • 如return指令
    • 根据这个指令的规则,由硬件自动去找到要找的操作数
  • 间接寻址方便子程序返回的原因
    • 如果没有间接寻址,返回地址就得写死在某个固定位置或者寄存器里,那就每次只能调用一次,不能嵌套调用、递归、重入,而间接寻址 + 栈就可以灵活处理这种情况:
      • 子程序每次调用都把返回地址压栈;
      • 返回时用间接寻址从栈中“取出”地址并跳转。
    • 间接寻址方便子程序返回,是因为它允许程序灵活地通过地址(比如栈指针)去间接找到“返回地址”,从而实现子程序嵌套调用和正确返回。
  • 数据的对齐和大/小端存放方式
    • 数据的对齐
      • 按边界对齐
      • 不按边界对齐
    • 存放方式
      • 大端存储(更符合人类视角)
      • 小端存储
      • 大端和小端只涉及多字节数据(如16bit、32bit、64bit)在内存中的存储顺序,指的是一个数据内部字节的顺序
        • 至于为什么指令格式是操作码+地址码,因为这是CPU指令格式的定义,与大小端存储无关,而且大小端存储针对的是《每个多字节数据》而不是整条指令
  • CISC和RISC的基本概念
    • 从机器级代码中可以看到显著区别
      • 指令字是否定长
        • RISC的指令定长,CISC的指令不定长
      • 除了Load、Store之外,是否还有其他的指令可以访存
        • RISC仅有Load、Store类指令,才可以访问主存
        • x86可以用move
  • 高级语言程序与机器及代码之间的对应
    • 编译器、汇编器、链接器的基本概念
    • 选择结构语句的机器级表示
    • 循环结构语句的机器级表示
    • 过程(函数)调用对应的机器级表示

解题方法

  • 先观察汇编语言,是x86还是MIPS?
    • 观察是否有注释
      • 通常来说,真题中x86汇编语言不会给太多注释(考研中默认大家懂x86)
    • 观察指令长度是否固定
      • x86属于CICS,指令长度不固定
      • MIPS属于RISC,指令长度固定
    • 观察寄存器名
      • x86的寄存器名为eax、ebx、ecx、edx
      • MIPS的寄存器名为R[0]、R[1]、R[2]
  • x86
    • 先搞懂C语言
      • 一般给出的x86机器级代码,第一列是指令的序号,第二列是指令的虚拟地址,第三列是指令的机器级代码(十六进制表示),第四列是汇编语言
    • 基于C语言的逻辑分析机器指令
      • 有没有分支结构
        • 观察有没有jxxx指令
          • 转移类指令除了操作码之外的部分,在x86语言中,就是偏移量PC的偏移量(注意取出当前指令以后PC要+“1”,偏移量是PC+“1”+偏移量)
          • 如果转移值是负数,要进行符号扩展到虚拟地址的位数,然后进行计算
      • 有没有循环结构
        • 观察有没有jxxx指令
        • 观察有没有loopxxx指令
      • 有没有函数调用
        • 观察有没有call指令
          • call 函数名 函数指令的地址
        • 观察有没有ret指令
      • 需不需要访问函数调用参数?观察在汇编语言中如何访问调用参数
        • 注:函数调用参数一般在[ebp+8]、[ebp+12]等位置
      • 需不需要定义局部变量?观察在汇编语言中如何访问局部变量
        • 注:局部变量的存储地址一般在[ebp-4]、[ebp-8]、[ebp-12]等位置
  • MIPS
    • 先搞懂C语言
    • 基于C语言的逻辑分析指令
      • 有没有分支结构
        • 观察有没有bxxx指令
          • 注:MIPS汇编语言中,转移类指令是以b开头的,表示”branch“,即分支。指令原理与x86的jxxx相同
      • 有没有循环结构
        • 观察有没有bxxx指令
      • 有没有函数调用
        • 注:MIPS汇编语言指令通常不考函数调用,重点关注x86的函数调用
    • 考不考流水线
      • 对指令进行分类
        • 五类指令,每一类指令的运行原理要搞清楚
        • 安排指令流水线
        • 分析数据冒险、控制冒险

基础知识

  • x86汇编指令(重点关注intel格式)

    • 算数、逻辑运算类指令
      • 加减乘除、左移右移等指令
    • 分支结构(if/else)
      • 无条件转移(goto)
        • jmp(无条件转移指令),类似于C原的goto
        • 无条件转移的指令只有 call,return和jmp 指令
      • 条件转移指令(if/else)
        • cmp A,B(本质是A-B,A-B的标志位信息会存放到PSW中)
          • 无符号数减法关注CF、ZF
            • A==B——需满足ZF ==1
            • A!=B——需满足ZF ==0
            • A>=B——需满足CF==0
            • A<=B——需满足CF==1|| ZF ==1
            • A>B——需满足CF==0 && ZF ==0
            • A<B——需满足CF==1
          • 有符号数减法关注OF、SF、ZF
            • A==B——需满足 ZF ==1
            • A!=B——需满足ZF==0
            • A>=B——需满足SF==OF
            • A<=B——需满足SF!=OF || ZF ==1
            • A>B——需满足SF==OF && ZF ==0
            • A<B——需满足SF!=OF && ZF==0
        • jxxx(转移指令)
          • 转移类指令,通常采用相对寻址。用补码表示偏移量。补码的值通常意味着PC要 往前/往后 跳多少个地址(注意:偏移量的单位可能是字节,也可能是指令字长)
          • x86属于CISC,指令字长不固定,因此转移类指令中一定是以”字节“为单位描述PC的偏移量
          • 偏移量一般用补码表示
    • 循环结构(for、while)
      • 条件转移指令jxxx可以用于实现循环
        • 注:在x86汇编语言中,循环结构通常是用jxxx指令实现的,很少使用loopxxx指令
      • loopx指令
        • 原理同转移指令
    • 函数调用/返回
      • 调用指令
        • call< label >
          • 当前PC值压栈保存,再修改PC的值,跳转到< label >
          • 与无条件转移指令jmp的区别
            • call指令会将函数返回地址(当前的PC)入栈
      • 返回指令
        • ret
          • 恢复程序计数器PC,返回原函数
  • MIPS汇编指令

    • 常见指令

      • 算数、逻辑运算类指令
          • OP:add
          • OP:sub
          • OP:mul
          • OP:div
        • 左移
          • OP:shl
        • 右移
          • OP:shr
      • 转移类指令
        • bxxx
          • b开头,意思是branch
          • 转移类指令,通常采用相对寻址。用补码表示偏移量。补码的值通常意味着PC要往前/往后跳多少个地址(注意:偏移量的单位可能是字节,也可能是指令字长)
          • MIPS属于RISC,指令字长固定,因此转移类指令中可能会以”指令字长“为单位描述PC的偏移量
      • Load类指令
        • lxxx
          • L开头,意思是load
          • 根据读取长度不同,”xxx“部分可能会变,例如从内存中读取一个字可以使用指令lw,这里的w指”word“
      • store类指令
        • sxxx
          • s开头,意思是store
          • 根据存储长度不同,”xxx“部分可能会变,例如往内存写一个字可以使用指令sw,这里的w指”word“
    • 结合五段式指令流水线考察

      • 数据冒险
        • 什么指令可能导致数据冒险
          • 前面的指令
            • 写某个寄存器
              • WB,第五个段
          • 后面的指令
            • 读(同一个)寄存器
              • ID,第二个段
          • 分析思路
            • 一条一条指令从前往后分析,如果一条指令写了某个寄存器,则观察与之相邻的后3条指令中,有没有哪条指令需要读同一个寄存器
      • 控制冒险
        • 什么指令可能导致控制冒险
          • 转移类指令
            • M,第四个段改变PC值
            • 处理控制冒险的方法:停三个周期,再取下一条指令(IF段),就不会发生控制冒险
          • 注:call、ret也属于转移类指令,也会在M段修改PC值

x86汇编语言,AT&T和intel格式的区别

  • 截至目前,历年真题中都是intel格式

指令的硬件实现

  • 标红的是考察的重点

  • 考察具体的某一条指令在执行的过程当中,如何用硬件去实现相应的这些功能

大纲

CPU的功能和基础结构

  • CPU=控制器+数据通路
    • 控制器,即CPU,是指令的控制部件
    • 数据通路,是指令的执行部件,包括运算器、寄存器、数据传输的线路等

指令的执行过程

  • 取值、译码
    • 取指令送入IR、PC+“1”
    • CU根据IR译码,确定指令的执行时序
  • 执行
    • 取操作数、运算、存结果
    • 异常/终端的检测与处理(如果有的话)

数据通路的功能和基本结构

控制器的功能和基本结构

控制器的功能和工作原理

  • 硬布线控制器
  • 微程序控制器
  • 这部分2022大纲更新后考察权重被弱化

异常和中断机制

  • 异常和中断的基本概念
  • 异常和中断的分类
  • 异常和中断的检测与响应

指令流水线

  • 指令流水线的基本概念
  • 指令流水线的基本实现
  • 结构冒险、数据冒险、控制冒险的处理
  • 超标量和动态流水线的基本概念

多处理器基本概念

  • SISD、SIMD、MIMD、向量处理器的基本概念
  • 硬件多线程的基本概念
  • 多核处理器(multi-core)的基本概念
  • 共享内存多处理器(SMP)的基本概念

解题思路

分析框架

  • 直接看硬件图很容易看晕,应该从指令要干什么(取值阶段和执行阶段)出发,然后考虑哪些部件实现这些功能,数据该如何流动,然后再去硬件中找实现这些功能的硬件和电路,让数据按照规划路线流动,即从人类的视角逆推而不是按照机器的电路,建立好思路再去看线路图

  • 思考:一条指令的执行过程

    • 取指阶段(所有指令在取指阶段做的事情都一样)

      • 根据PC从主存中取指令到IR
        • PC→MAR
        • M(MAR)→MDR(指令)→IR
      • PC+“1”
        • 可以考虑用硬件①、②实现PC+”1“
        • 也可以考虑用硬件⑿实现PC自增
        • 注意这个“1”到底是多少,与指令字长有关,与编址方式也有关系,注意区分字节编址和字编址
    • 执行阶段(根据指令类别来思考)

      • 数据传送类指令(如:mov、load、store)

        • 关注数据从哪里流动到哪里
          • 主存
            • 读数据数据
              • 将要读的地址写入MAR
              • 给CU主存发出读信号
              • 数据被读到MDR寄存器当中
              • 把MDR里的数据转移到最终放的位置
            • 写数据
              • 数据放入MDR当中
              • CU发出写信号把MDR里的值写到MAR所指的主存单元
            • 注意:关注总线占用问题(总线是临界资源,同一时刻只能由一个设备占用,可用暂时寄存器存放数据)(安排控制信号)
          • 寄存器
          • 立即数
      • 运算类指令

        • 加、减
          • 可以考虑用硬件①②(ALU和加法器)
          • 自增++、自减–可以考虑用硬件⑿(自增功能寄存器)
          • 可以考虑用硬件①③(ALU和乘法器)
          • 特殊乘法,乘以2^n,可用⑾(移位寄存器)左移功能平替,也可以用①ALU的左移功能平替
          • 可以考虑用硬件①(ALU)④(除法器)
          • 特殊除法,除以2^n,可用⑾(移位寄存器)右移功能平替,也可用①ALU的右移功能平替
        • 移位运算
          • 可以考虑用硬件①(ALU)⑾(移位寄存器)实现
        • 与、或、异或等双操作数逻辑运算
          • 可考虑用硬件①(ALU)实现
        • 非 运算
          • 可以考虑用硬件①(ALU,只需要一端有输入)、⒂(带取反功能的寄存器,在减法当中可能用到,主要按位取反)实现
        • 短数→长数
          • (如偏移量和基值的加减)
          • 带符号数位扩展可使用硬件⒀(带符号扩展功能的寄存器)
          • 无符号数位扩展可使用硬件⒁(带零扩展功能的寄存器)
      • 转移类指令

        • 重点关注13年真题

        • 条件转移

          • 做条件判断时,经常用到减法生成标志位,因此可能会用到硬件①(ALU)或者②(加法器)
          • 减法生成的CF、ZF、SF、OF标志位需要用”标志寄存器“存储起来
          • 然后jxxx指令往往根据标志位来判断是否满足条件,然后进行转移,也就是修改PC值
        • 无条件转移

        • 无论是条件转移还是无条件转移,在计算转移目标地址时,需要将”PC+偏移量”(jxxx指令中会给地址码,地址码指明了偏移量,而且偏移量一般是补码)。因此可以考虑使用硬件①(ALU)或者②(加法器)实现这个加法运算

          • 注意偏移量以字节还是字为单位
            • 在CISC中不可能以指令字为单位,因为指令不定长,PC值是按字节编址
  • key:指令各阶段的功能,要用什么硬件部件实现?数据应该如何流动?——硬件的控制信号、连线只是实现逻辑的一种外化体现

如何用硬件实现指令功能

  • 控制硬件的信号

    • XXXin
      • 允许数据进入
    • XXXout
      • 允许数据出去
    • XXXop
      • 具体的控制信号
  • 常见硬件

    • 操作元件(组合逻辑元件)

      • ①算术逻辑单元(ALU)

        • 实现加减乘除等算术运算、与或非等逻辑运算
        • 注意:ALU可以支持“直送”,即不对输入数据做任何处理
        • 控制信号:n bit,取决于ALU支持多少种运算,n bit控制信号,对应2^n种运算
      • ②加法器(Adder)

        • 结合第二章复习,相当于弱鸡版ALU,只能实现加法、减法
        • 控制信号:1bit
          • 0加1减,控制信号的值会影响CF标志位(CF=sub⊕最高位进位)
      • ③乘法器

        • 实现乘法运算
      • ④除法器

        • 实现除法运算
      • ⑤三态门

        • 就是一道门,可开可关
        • 控制信号:1bit,用于控制开/关
      • ⑥多路选择器(MUX)

      • 有2^n路输入,通过n bit控制信号选择让其中的某一路输入通过

      • 控制信号:n bit,取决于有多少路输入,n bit控制信号,对应2^n路

      • ⑦译码器(Decoder)

        • 根据n bit控制信号选择将2^n条输出线路中的某一条信号选通为1
        • 控制信号:n bit。由译码器的n bit输入即可决定选通哪根线
      • 多路选择器是一种能够从多个输入信号中选择一个输出的信号转换设备。译码器是一种将二进制编码转换为一组特定输出信号的设备。在寄存器堆中,译码器常常用于地址译码,从而控制对特定寄存器的访问。

        • 执行寄存器写相关指令时(将结果保存在寄存器中),指令中的寄存器编号被送到一个地址译码器进行译码,选中某个寄存器进行写入,读出时(从寄存器中获得计算数据)寄存器编号作为一个控制信号来控制一个多路选择器,选择相应的寄存器读出。
        • 读口(组合逻辑操作):无需时钟控制,busA和busB分别由RA和RB给出地址。地址RA或RB有效后,经一个“取数时间(AccessTime)”,busA和busB有效
        • 写口(时序逻辑操作):需要时钟控制。写使能为1的情况下,时钟边沿到来时,busW传来的值开始被写入RW指定的寄存器中。
          • 写是需要改变寄存器的值的,需要等到数据的信号稳定后写入,防止出错。而读只需要和寄存器里的值相等就行
    • 存储元件(时序逻辑元件)

      • 程序员可见的寄存器就是程序员想改什么值就改什么值的寄存器

        • PC(转移类指令),普通寄存器
      • MAR、MDR、IR、暂存寄存器对程序员不可见

      • ⑧普通寄存器

        • 具有特定用途的寄存器
          • MAR
      • MDR

        • 存取单位为xx位,意味着MDR为xx位
      • IR

        • xx位定长指令字结构→IR指令寄存器的位数是xx位
      • PC

      • FR(即标志寄存器,有的系统中称为PSW)

      • 控制信号:允许读、允许写

    • ⑨暂存寄存器

    • 通常会在单总线结构中和ALU配合使用

      • 控制信号:允许读、允许写

      • ⑩通用寄存器组

        • 控制信号1:允许读、允许写
        • 控制信号2:若通用寄存器组共有2^n个寄存器,则需要n bit控制信号选择其中一个寄存器
      • 注意:有的寄存器可能支持特殊功能

        • ⑾带“移位功能“的寄存器
          • 可以实现移位运算(算术左移/右移、逻辑左移/右移)
          • 隐藏技能:用移位运算等价实现”乘/除2^n“
        • ⑿带”自增、自检功能“的寄存器
          • 可实现寄存器的值++、-
          • 常见用途:有的PC寄存器带有自增功能
    • 带其他特殊功能的寄存器

      • ⒀带”符号扩展“功能的寄存器

      • 可以将位数短的带符号数(补码)扩展为位数更长的带符号数(补码)

      • ⒁带”零扩展“功能的寄存器

        • 可以将位数短的无符号数扩展为位数更长的无符号数
      • ⒂带”取反功能“的寄存器

        • 可以实现全部位按位取反

如何看懂注释

袁版注释风格(16年以后的真题)
  • 数据在通用寄存器组
    • 特点:用R[i]表示,中括号内为寄存器编号
    • eg:R[6] x R[3]→R[2]。将寄存器R6 x R3的内容写入R2
  • 数据在某个特殊名字的寄存器
    • 特点:直接用寄存器名字表示
    • eg:R[6]+10→PC。把寄存器R6+10的内容写入PC
    • eg:MDR→PC。把MDR的内容写入PC
  • 数据在主存
    • 特点:用M[addr]表示,addr为主存地址
    • eg:R[6]→M[10086]
      • 将R6的内容存入主存地址10086
    • eg:R[3]<<2→M[R[6]]
      • 将R3进行”左移两位“运算,并将结果存入R6所指的主存地址(寄存器间接寻址)
    • eg:M[PC+10]→MDR
      • 将PC+10指向的主存内容取到MDR
  • 特点总结
    • 喜欢用”中括号“,中括号里边表示寄存器编号、或主存地址
    • 要使用寄存器里的内容,不用加小括号,直接给寄存器名即可
唐版注释风格(16年以前的真题)
  • 数据在通用寄存器组
    • 特点:箭头左边,用寄存器名加小括号,表示寄存器里的值;箭头右边表示存入某个寄存器不加小括号
    • eg:(R6)x(R3)→R2。将寄存器R6xR3的内容写入R2
  • 数据在某个特殊名字的寄存器
    • 特点:直接用寄存器名字表示,要使用寄存器里的值需要加小括号
    • eg:(R6)+10→PC。把寄存器R6+10的内容写入PC
    • eg:(MDR)→PC。把MDR的内容写入PC
  • 数据在主存
    • 特点:用M(addr)表示,addr为主存地址
    • eg:(R6)→M(10086)
      • 将R6的内容存入主存地址10086(寄存器间接寻址)
    • eg:(R3)<<2→M((R6))
      • 将R3进行”左移两位“运算,并将结果存入R6所指的主存地址(寄存器间接寻址)
    • eg:((PC)+10)→MDR
      • 将PC+10指向的主存内容取到MDR
      • 在箭头的左边表示“访问主存”时,可以不加M,用两层括号表示寄存器间接寻址
  • 特点总结
    • 喜欢用“小括号”,但是箭头所指的零一表会少一层小括号
    • 箭头左边若要使用某个寄存器里的内容,必须在寄存器名字外面加小括号
    • 箭头右边表示存入某个寄存器,不用加小括号

指令流水线

考研大纲要求&备考方法

  • 结构冒险:由于多条指令在同一时刻争用同一资源而形成的冲突
  • 数据冒险:在一个程序中,存在必须等前一条指令执行完才能执行后一条指令的情况,则这两条指令即为数据相关
  • 控制冒险:当流水线遇到转移指令和其他改变PC值的指令而造成断流时,会引起控制相关

结构冒险及其处理

  • 第一个阶段即取指阶段和第四个阶段访存阶段都需要使用到同一块Cache,因此多条指令连续执行的时候就会出现结构冒险,比如第四个始终,IF取指阶段需要访问Cache,M访存阶段也需要访问Cache

  • 让后面的一条指令阻塞若干个时钟周期,直到这条指令的执行不会导致结构冒险为止。

  • 将Cache分为指令Cache和数据Cache,分别存储指令和数据,第四个时钟的时候IF访问指令Cache,M访问数据Cache

真题实战

  • 在考研真题中,只要遇到五段式指令流水线,都可以默认在这个系统当中,它的数据Cache和指令Cache一定是分离的,不需要去考虑结构冒险的问题

如何分析数据冒险、控制冒险

  • 任何一条指令对寄存器的写操作都一定是在WB这一段
  • 任何一条指令对寄存器的读操作一定是在ID这一段
  • 会写寄存器的只有load指令和运算类指令,而且一定是在第五个段WB这个段
  • 如果前一条指令写了某个寄存器,而后一条指令在前一条指令还没写回之前,就去尝试读同一个寄存器的值,那么就会发生数据冒险
    • 因此在分析题目的时候,需要注意观察load指令和运算类指令有没有写某一个寄存器
    • 如果发现了这种情况,那么接下来就需要观察这两类指令的后面几条指令有没有去读同一个寄存器,如果有的话,就有可能发生数据冒险
  • 所有的指令当中,只有转移类的指令有可能会更改PC值,而改变PC值在转移类指令的第四个阶段即访存阶段
  • ID:读寄存器,WB:写寄存器

数据冒险的分析和处理

  • 检查往后3条指令是否读了同一个寄存器

    • 如何判断一条指令是否写了某一寄存器
      • 注释,箭头一侧是不是寄存器
    • 如何判断一条指令是否读了某一寄存器
      • 除了被写的寄存器之外,注释中出现的寄存器名都是要被读的
        • 写主存的时候,其他寄存器都是读
  • 旁路技术如何解决数据冒险:

    • 一般来说,任何一条指令都是在ID段读取数据,EX这一段交给ALU去运算取出来的数,等EX段执行结束之后,就得到了计算的结果,只不过这个运算结果需要等到WB第五个段的时候才会被写回寄存器
      • 对于下一条指令来说,它需要上一条指令的运算结果去进行进一步的处理
      • 在旁路(转发)技术的情况下,可以在上一条指令得到运算结果之后,直接用一条特殊的线路,把运算结果转发给后一条指令的ALU,由于采用了转发技术,因此在EX这个段使用的数据是正确的,因此下一条指令不需要有任何的流水线阻塞
    • 因此如果题目中明确告诉我们采用了旁路技术或者转发技术,那么绝大多数的数据冒险都不需要产生硬件阻塞
  • 不能解决Load-Use数据冒险

    • load的作用是把数据从内存读到寄存器里面,load-use是指load把数据从内存读到寄存器里面,紧接着下一条指令就需要去使用寄存器里面的值
    • 以本题为例,I2指令是load指令,会从内存里面读出数据然后写回R[s3],然后后面紧跟的I3指令又会使用到R[s3]这个寄存器里面的值,所以I2和I3之间就形成了load-use数据冒险
      • 而load指令读数据是在M访存阶段完成的,然后在WB段会把从内存里面读出来的这些数据写回寄存器。因此I2这条指令要M访存阶段结束后才可以从内存里面获得最终的这个数据,然后WB这个段再把数据写回R[s3],后一条指令I3需要在EX阶段使用load指令读出来的这个数据,因此这个EX段必须安排在I2的M段之后,所以我们不得不让EX段阻塞一个时钟,等到阻塞一个时钟之后才能使用转发技术,在I2的M段把数据转发给I3的EX段

阻塞的实例

  • 2016

  • 注意:当I3没有进入到ID这一个阶段的时候,I4是不可以进入IF这一段的,因为在I3阻塞的这几个始终之内,I3一直占用着IF这个段的硬件资源,只有他真正地进入下一段ID段,那么IF这个段的硬件资源才会被释放,给下一条指令使用。

  • 2019

  • 前一条指令进入下一个阶段之前,会一直占用当前阶段的硬件资源

转发技术实例

  • 旁路技术如何解决数据冒险:

    • 一般来说,任何一条指令都是在ID段读取数据,EX这一段交给ALU去运算取出来的数,等EX段执行结束之后,就得到了计算的结果,只不过这个运算结果需要等到WB第五个段的时候才会被写回寄存器
      • 对于下一条指令来说,它需要上一条指令的运算结果去进行进一步的处理
      • 在旁路(转发)技术的情况下,可以在上一条指令得到运算结果之后,直接用一条特殊的线路,把运算结果转发给后一条指令的ALU,由于采用了转发技术,因此在EX这个段使用的数据是正确的,因此下一条指令不需要有任何的流水线阻塞
    • 因此如果题目中明确告诉我们采用了旁路技术或者转发技术,那么绝大多数的数据冒险都不需要产生硬件阻塞
  • 不能解决Load-Use数据冒险

    • load的作用是把数据从内存读到寄存器里面,load-use是指load把数据从内存读到寄存器里面,紧接着下一条指令就需要去使用寄存器里面的值
    • 以本题为例,I2指令是load指令,会从内存里面读出数据然后写回R[s3],然后后面紧跟的I3指令又会使用到R[s3]这个寄存器里面的值,所以I2和I3之间就形成了load-use数据冒险
      • 而load指令读数据是在M访存阶段完成的,然后在WB段会把从内存里面读出来的这些数据写回寄存器。因此I2这条指令要M访存阶段结束后才可以从内存里面获得最终的这个数据,然后WB这个段再把数据写回R[s3],后一条指令I3需要在EX阶段使用load指令读出来的这个数据,因此这个EX段必须安排在I2的M段之后,所以我们不得不让EX段阻塞一个时钟,等到阻塞一个时钟之后才能使用转发技术,在I2的M段把数据转发给I3的EX段

补充旁路转发和load-use

  • load指令在第四周期即M访存阶段结束时,数据在流水段寄存器中已经有值,在此时可以采用数据转发技术可以使load指令后面第二条指令得到所需的指,但不能解决load指令和随后的第一条指令之间的数据冒险,要延迟执行一条指令

  • 普通数据冒险采用旁路转发技术不需要阻塞,而load-use需要阻塞一个时钟周期的根本原因是所需数据产生的阶段不同,使用ALU计算获得结果(所需数据)的在EX阶段可以产生所需数据,而load指令在M阶段从内存中获取所需数据
    • load指令中的ID和EX是在获取所需数据的内存地址,M访存获得所需数据

控制冒险的分析和处理

  • 转移类指令:call、return、jmp、jxxx

  • PC值的修改是在M访存阶段,所以如果我们能让后一条指令的执行阻塞三个时钟,让其IF取指阶段在转移类指令的第四段之后再开始,在修改PC之后,这样就可以保证后一条指令取指的动作一定是取到了正确的指令

    • 只有修改完PC之后才能知道下一条指令真正的地址在什么地方,这样可以取执行后一条指令的IF段
  • 有条件转移(M,第四个段改变PC)

    • EX段进行条件是否满足的判断;M若满足条件则改变PC的值,不满足则不改变PC的值(此时也不会发生控制冒险),第五个段为空段
    • 停3个周期,再取下一条指令,就不会发生控制冒险
  • 无条件转移(EX,第三个段改变PC)

    • EX就写回PC,M和WB为空段

    • 停2个周期,再取下一条指令,就不会发送控制冒险

阻塞实例

I/O总结

复习思路

  • 一个具体的I/O过程,既有软件又有硬件,那么软件是怎么控制硬件的,硬件又是怎么反馈到软件的
  • I/O控制方式是计组重点,设备驱动程序是操作系统的重点

  • 中断过程是整个I/O操作的基础
  • 从轮询、中断、DMA三种方式介绍printf的具体实现
  • 理解一个具体的I/O过程的时候,要从两方面理解,第一方面就是硬件,第二方面就是软件
    • 机组强调的就是硬件的三种I/O控制方式
    • 操作系统更偏向于各个软件层次是怎么工作的
  • 不论是什么I/O控制方式,也不论是哪一个层次的I/O软件,本质上就是两个维度,即一、CPU是怎么和设备控制器之间交换信息的,二、设备控制器是怎么和I/O设备交换信息的。

中断

深入理解中断机制——从中断控制器出发

  • 中断的目的:CPU和外设并行工作
    • 假如现在我的某一个进程p正在运行,然后我要启动某一个I/O操作,那么当进程p启动外设工作的时候,那我这个CPU就进行一个进程调度,调度进程q来执行(因为此时进程p需要的外设数据还没有准备好,无法继续运行,因此让另一个进程来工作),当cpu在执行进程q的时候,外设也在准备数据
    • 到了外设准备完数据之后,会向CPU发出一个中断请求,那么CPU响应这个中断之后就终止现行程序的执行CPU,经过一个中断响应,然后调出这个中断服务程序来执行。
    • 中断服务程序执行的过程中,又启动外设工作,然后CPU返回执行的程序。

处理器中的中断处理机制

  • 中断响应(也是中断隐指令的工作,硬件完成):
    • 保护断点和程序状态

      • 保存PC和PSW(断点)
    • 识别中断事件

      • 软件识别

        • 核心思想是操作系统是一个统一的异常处理程序,用程序来完成的,识别出异常事件。
      • 硬件识别

        • 向量中断方式

        • 用专门的硬件查询电路识别,得到一个中断类型号,然后根据这个中断类型号到中断向量表里面读取相应的中断服务程序的入口地址(中断向量)

          • 硬件:PIC(可编程中断控制器)
    • 切换到具体的中断处理程序执行

  • 关中断:将中断允许标志(在PSW里面)清零,表示关中断(首先要做的基本操作)

中断处理优先级和中断响应优先级的区别

  • 中断响应优先级是针对同时到达的中断请求先处理谁的规定。比如A、B同时向CPU发出中断请求,而中断响应优先级是A>B,那么CPU就会先处理A,再处理B。如下图

  • 中断处理优先级是解决中断嵌套情况下优先处理谁的问题。比如A、B两个中断的中断处理优先级是B>A,如果当CPU正在处理中断请求A时,B向CPU发送了中断请求,那么CPU会先暂停处理A,转而处理B,B结束后再继续处理A。如下图

  • 在同一个系统中中断响应优先级A>B和中断处理优先级B>A是不冲突的。因为他们针对的情况不可能同时发生。
    • A、B同时到达,由中断响应优先级决定先执行谁,A、B不同时到达在发生中断嵌套时由中断处理优先级决定先执行谁。

中断控制器的基本结构

  • 上图的解释如下:

    • 右下角中断请求寄存器,相当于IRR(Interrupt Request Register) 中断请求寄存器
      • 外设通过硬件将中断请求信号(INTx)送入此寄存器。每一位对应一个中断源,置1表示对应设备请求中断。
      • 每个外设或中断源会在中断请求寄存器(IRR)中设置相应的位,以表示中断请求的发生。该寄存器保存了所有待处理的中断请求。
    • 左下屏蔽寄存器,相当于IMR(Interrupt Mask Register)
      中断屏蔽寄存器
      • CPU 通过 I/O 操作设置该寄存器的值,用于屏蔽(忽略)某些中断请求。IMR 对 IRR 做“与”操作,决定哪些中断请求有效。
    • 判优线路,相当于优先级编码器/优先级解析器(Priority Resolver)
      • 如果多个中断请求同时有效,则根据预定优先级选择一个最高优先级的中断进行处理。
    • 向量地址形成线路,相当于ISR(Interrupt Service Routine)地址形成逻辑
      • 根据判优结果给出中断类型对应的中断向量地址(中断号),CPU 据此跳转到相应的中断服务程序(ISR)。
    • 右上INTR信号线,相当于中断请求信号线
      • 若屏蔽寄存器放行且有请求,PIC 向 CPU 发出中断请求(INTR)信号。CPU 在执行完一条指令后查询此信号。
    • 左中CPU发出终端查询,相当于中断确认(Interrupt Acknowledge)信号
      • 当 CPU 检测到 INTR 有效时,会发出查询信号给 PIC,请求中断类型号/向量地址。
    • CPU
      • 响应中断,保存现场,跳转到中断服务程序执行,返回继续执行原程序。
  • 注意区分中断响应优先级和中断处理优先级

    • 多个设备发出中断请求的情况下,先是中断处理优先级来决定能不能通过请求信号,然后响应优先级处理同时到达的请求信号的响应优先级,也就是谁先开始中断处理,然后再根据中断处理优先级,决定是否打断当前的中断程序换成另一个中断请求信号进行中断处理
      • PIC 处理中断嵌套的核心流程:
      • 步骤 1 (屏蔽): 所有中断请求首先经过 IMR (基于当前处理优先级) 的过滤。只有未被屏蔽的请求才能成为“有效中断请求”。
      • 步骤 2 (仲裁): PIC 在所有当前有效的中断请求中,根据中断响应优先级选择一个优先级最高的。
      • 步骤 3 (响应): PIC 将步骤 2 选出的最高优先级中断请求发送给 CPU (INTR 有效)。CPU 响应后,开始执行该中断的 ISR。
      • 步骤 4 (嵌套判断): 在新 ISR 执行过程中,步骤 1 (屏蔽) 再次发挥作用。如果此时有更高处理优先级的中断请求是有效的(并且未被当前 ISR 的 IMR 屏蔽),那么该更高优先级中断可以打断当前正在执行的低优先级 ISR,流程回到步骤 2 和 3(选择并响应那个更高优先级的中断)。
  • PIC具体的工作流程

    • (1)假设目前有ABC三个工作人员,并且ABC的中断响应优先级A>B>C,中断处理优先级C>B>A

    • (2)假设现在CPU正在执行这个中断源A的中断服务程序,中断源B和中断源C同时发出这个中断请求

      • 中断请求来自外设,由硬件直接设置这个中断请求寄存器
        • eg:中断请求寄存器里面有跟设备b对应的1bit,如果这个bit位变成了1,那么就代表此时中断源b发出了中断请求
    • (3)当CPU执行完一条指令之后,去检查INTR(中断请求信号寄存器),检测其是否有效,如果有效,认为此时系统中有别的中断需要处理,那么CPU就会响应别的中断

      • 寄存器是否有效取决于这个中断请求寄存器里面的一个有效中断信号能不能送过来
      • 而能不能送过来取决于这个中断屏蔽寄存器有没有把这个中断请求寄存器里面的信号屏蔽掉
    • (4)因为处理优先级C>B>A,因此中断屏蔽寄存器里面A所设置的值是屏蔽不了B和C的

    • (5)因为当前执行的是中断源A的中断服务程序,这个中断屏蔽寄存器里面的值就由中断服务程序A来设置的

    • (6)因为处理优先级C>B>A,所以A是屏蔽不了C和B发过的中断请求的,当一个指令周期结束后,那么B和C的有效的这个中断请求就会使这个INTR寄存器有效,并将请求发给CPU,那么CPU就会检测到这个中断请求信号

      • 所以CPU能不能检测到这个中断请求信号有效是由中断处理优先级来决定的,中断处理优先级来决定设置的屏蔽寄存器,屏蔽寄存器来决定的就是中断请求寄存器里面的有效中断源发出的信号,能不能使这个INTR有效。
      • 更精确地说: CPU 检测到的 INTR 信号是否有效,是由 PIC 根据当前正在执行程序(主程序或 ISR)设置的中断屏蔽寄存器 (IMR) 以及 中断请求寄存器 (IRR) 的状态共同决定的。而 IMR 的设置策略正是基于中断处理优先级(嵌套优先级)
      • 在你的场景中,因为 A 的处理优先级最低 (A),它的 IMR 允许 B 和 C 通过,所以 B 和 C 的请求能让 INTR 有效。如果当前执行的是 C 的 ISR (处理优先级最高 C),它的 IMR 会屏蔽 B 和 A,那么 B 和 A 的请求就无法让 INTR 有效。
    • (7)CPU检测到这个中断信号有效,那CPU就会发出一个中断查询信号。

    • (8)中断查询信号决定CPU当前要响应中断,因为CPU目前只知道有终端要处理,但是不知道有多少个。

    • (9)当B和C都发出中断请求,两个请求同时送到判优电路,然后由这个判优电路来决定CPU要响应哪一个中断源的中断请求

    • (10)这个判优电路是由中断响应优先级来设计的,决定了当有多个中断源的中断请求被送到这个判优电路的时候,CPU应该响应哪一个中断源的中断请求。

    • (11)由于B的响应优先级>C,所以当B的中断请求和C的中断请求同时到来的时候,判优电路和向量地址形成线路就会形成一个中断源B的向量地址,然后将中断源B的向量地址送到CPU,然后CPU响应B的中断并且处理B的中断,所以先响应的是B的中断

    • (12)响应B的中断之后就会执行B的中断服务程序,由这个B来设置新的屏蔽字

    • (13)那么当B设置完这个屏蔽字,保存现场之后,就会进行一个开中断(多重中断系统的情况下)

    • (14)由于C的中断请求尚未被处理,并且C的处理优先级大于B,那么当执行B的中断服务程序的时候,又会响应C发过来的中断请求,CPU检测到这个INTR有效,发出查询。C的中断请求被送到判优电路的时候,只有C一个中断源,那么就会响应并处理这个中断源C的中断请求。

      • 核心争议点:仲裁发生在什么时候?是在INTR有效之前还是之后?

        1. 网课描述流程 (更精确的硬件时序):
          • 步骤1 (中断发生 & 屏蔽): B和C同时发出请求 -> IRR相应位置1。
          • 步骤2 (屏蔽检查 & 置位INTR): PIC检查IMR。因为A的ISR设置的IMR未屏蔽B和C -> B和C的请求都是有效请求。PIC立刻INTR信号置为有效(拉高),通知CPU“有中断请求待处理”。此时PIC内部尚未最终决定响应B还是C!
          • 步骤3 (CPU响应 & 发出INTA): CPU执行完当前指令,检测到INTR有效 -> CPU在当前中断周期(或下个周期)发出第一个INTA脉冲给PIC,表示“我收到了,请告诉我中断号”。
          • 步骤4 (PIC内部仲裁 & 锁定): PIC收到第一个INTA脉冲后,立刻在其当前所有有效中断请求(B和C)中进行仲裁(根据响应优先级 A > B > C,此时B的响应优先级高于C)。
          • 步骤5 (PIC发送向量号): PIC将仲裁获胜者(B)的中断向量号放到数据总线上(通常在CPU发出的第二个INTA脉冲期间)。
          • 步骤6 (CPU处理): CPU读取向量号,保存现场,跳转到B的ISR。
        2. 我之前描述的简化流程 (侧重逻辑概念):
          • 我在描述中说“PIC现在有B和C两个有效中断请求。它根据中断响应优先级进行仲裁… PIC选择响应优先级最高的B,将其请求发送给CPU(设置INTR信号有效)”。这里的“发送请求/设置INTR”隐含了仲裁完成的意思,这在逻辑上是正确的(INTR有效意味着有*最高优先级*的有效请求存在),但在精确的硬件信号时序上,仲裁是在INTA到来时才最终完成并锁定结果的。

        为什么网课的流程(先置INTR有效,等INTA到来再仲裁)是合理的?

        1. 速度考虑: 仲裁电路需要一点时间。让PIC在检测到任何有效请求时就立即置位INTR,可以尽早通知CPU“有中断来了”,让CPU尽快结束当前指令周期。CPU发出INTA需要时间,PIC可以利用这段时间完成仲裁。
        2. 请求的动态性: 在CPU响应(发出INTA)之前,中断请求状态可能发生变化(例如,一个更高优先级的请求突然到达)。如果在检测到请求时就立刻仲裁并锁定结果,可能会错过这个新到的更高优先级请求。在收到INTA时才进行最终仲裁,可以保证响应的是在INTA时刻存在的、优先级最高的有效请求。 这是中断处理中“优先级”定义的基石。
        3. 硬件实现: 8259A PIC正是这样工作的。它有一个“在服务寄存器”用于跟踪当前正在处理的中断。收到INTA信号是PIC开始“处理”这个中断(包括仲裁、置位ISR、发送向量号)的正式触发点。

        总结流程 (结合网课和硬件细节):

        1. 中断请求 & 记录: 外设触发 -> PIC设置中断请求寄存器对应位。
        2. 屏蔽检查 & 通知CPU: PIC检查中断屏蔽寄存器 -> 对于未被屏蔽的请求,PIC立即置位INTR信号(有效),通知CPU“有(一个或多个)有效中断请求待处理”。(此时PIC内部知道有B和C有效,但未决定响应谁)
        3. CPU检测 & 响应: CPU执行完当前指令 -> 检测到INTR有效 -> CPU向PIC发出INTA信号(中断响应信号)。
        4. PIC最终仲裁 & 锁定: PIC收到第一个INTA脉冲 -> 立即在其当前所有有效且未被屏蔽的中断请求中,根据中断响应优先级进行仲裁,选出优先级最高者 -> PIC锁定这个结果(通常通过设置“在服务寄存器”对应位)。
        5. PIC发送向量号: PIC将获胜中断源对应的中断向量号放到数据总线上(通常在CPU发出的第二个INTA脉冲期间被CPU读取)。
        6. CPU处理中断: CPU读取向量号 -> 保存当前执行现场 -> 跳转到该向量号对应的中断服务程序执行。
        7. (嵌套场景) 新中断到来: 如果在新ISR执行过程中,有更高处理优先级的中断有效且未被当前IMR屏蔽 -> 回到步骤2,INTR再次被置位有效,流程重复。

        结论:

        • 网课是对的。 在标准PIC(如8259A)的工作流程中:
          • PIC在检测到任何有效中断请求(通过IRR和IMR判断)时,会立即置位INTR信号通知CPU,而不等待仲裁结果
          • 最终的仲裁发生在CPU响应中断(发出第一个INTA信号)之后。 仲裁依据是中断响应优先级,目的是选出在INTA时刻存在的、优先级最高的有效中断请求。
          • 中断处理优先级(通过IMR设置)决定了哪些中断请求能成为“有效请求”,从而让INTR被置位。
        • 你的理解“先是B,C的响应请求使得INTR有效,CPU知道有中断请求后再将B,C送入判优电路,然后返回响应优先级高的向量地址给CPU” 完全准确地描述了硬件流程。
        • 我之前的描述在逻辑概念上(屏蔽->有效请求->仲裁选最高->响应)是正确的,但在精确的硬件信号时序(INTR置位发生在最终仲裁之前)上不够严谨。感谢你指出这个细节!

        简单来说:INTR有效 = “有事!”INTA到来 = “什么事?”PIC在回答“什么事?”(发送向量号)时,才通过仲裁确定具体是“哪件事”(哪个中断源)优先级最高。 而“处理优先级”决定了哪些“事”有资格喊“有事!”。

    • (15)等到响应完中断源C的中断请求之后,那么就会执行C的中断服务程序,当C的中断服务程序执行完之后,然后返回B,B执行完之后,又返回到A

    • (16)因此完成顺序是C→B→A,相应顺序是B→C

      • 中断响应优先级是由判优电路决定的,而中断处理优先级是由这个中断屏蔽字寄存器决定的,它是中断服务程序来设置的,只有检测到中断信号有效了才能进行下一步。
  • 中断号(向量地址)送到数据线上

  • 当前指令周期最后,每条指令执行完毕以后检测控制信号INTR,是否有请求信号,采样到信号有效,就立即进入中断响应中期

  • 中断响应过程中,当CPU检测到INTR信号有效,然后进入中断响应周期,然后过了一个固定的时间,它发出一个中断查询号(也叫中断回答信号),根据中断查询信号,那么所有未被屏蔽的中断进入判优电路,由判优电路来决定响应哪个中断信号。

  • 判优电路实际上是一个编码器,指令不能更改,一旦这个可编程的中断控制器PIC做好以后,并行判优电路就固定了,中断响应优先级也就固定了
PIC中断处理优先级与嵌套流程解析

PIC 微控制器的中断处理流程

在 PIC 微控制器中,中断处理是通过硬件和软件的协同工作来实现的。当多个中断请求同时发生时,系统会根据预设的优先级来决定处理顺序。

1. 中断请求寄存器(IRR)

每个外设或中断源会在中断请求寄存器(IRR)中设置相应的位,以表示中断请求的发生。该寄存器保存了所有待处理的中断请求。

2. 中断使能寄存器(IE)

中断使能寄存器(IE)控制着哪些中断请求被允许处理。如果 IE 寄存器中的某个位被设置为 1,则允许相应的中断请求被处理;否则,不处理。

3. 中断优先级

PIC 微控制器通常支持多级中断优先级,如高优先级和低优先级。优先级决定了中断被服务的顺序。高优先级的中断可以打断低优先级的中断。

4. 中断服务程序(ISR)

当中断被响应时,CPU 会保存当前状态,并跳转到相应的中断服务程序(ISR)。ISR 执行完毕后,CPU 恢复状态,继续正常执行。

核心概念澄清:

  1. 中断响应优先级 (IRQ Priority / Arbitration Priority):
    • 决定当多个中断请求同时有效时,PIC 选择哪个中断请求发送给 CPU(即谁先获得响应)。
    • 在你的例子中:A > B > C。意味着如果 A、B、C 同时有效,PIC 会优先将 A 的请求发送给 CPU。
  2. 中断处理优先级 / 嵌套优先级 (Interrupt Nesting Priority / Masking Priority):
    • 决定当前正在执行的中断服务程序 (ISR) 能够被哪些更高优先级的中断打断。
    • 这通过中断屏蔽寄存器 (IMR) 来实现。当前运行的 ISR 会在 IMR 中设置一个屏蔽位 (Masking Level),通常只允许比自己处理优先级更高的中断请求通过 PIC 到达 CPU。
    • 在你的例子中:C > B > A。这意味着:
      • 如果 C 的 ISR 正在执行,它设置 IMR 后,会屏蔽 B 和 A(甚至可能屏蔽所有更低优先级)。
      • 如果 B 的 ISR 正在执行,它设置 IMR 后,会屏蔽 A,但不会屏蔽 C
      • 如果 A 的 ISR 正在执行,它设置 IMR 后,不会屏蔽 B 和 C(因为 B 和 C 的优先级都比 A 高)。

针对你描述的场景分析:

  • 初始状态: CPU 正在执行中断源 A 的 ISR。
  • 事件: 中断源 B 和中断源 C 同时发出中断请求。
  • PIC 内部处理:
    1. 硬件设置中断请求寄存器 (IRR) 中 B 和 C 对应的位为 1。
    2. PIC 查看当前的中断屏蔽寄存器 (IMR)。因为当前正在执行 A 的 ISR (处理优先级最低 A),其设置的 IMR 不会屏蔽处理优先级更高的 B (B > A) 和 C (C > A)。
    3. 因此,B 和 C 的中断请求都能通过 IMR 的检查,成为有效请求。
    4. PIC 现在有 B 和 C 两个有效中断请求。它根据中断响应优先级 (A > B > C) 进行仲裁。注意:这里响应的是 B 和 C,A 已经在服务中,不再参与此轮仲裁。
      • 响应优先级 B > C(因为 A > B > C 意味着 B 的响应优先级高于 C)。
      • PIC 选择响应优先级最高B,将其请求发送给 CPU(设置 INTR 信号有效)。
  • CPU 响应:
    1. CPU 完成当前指令(A 的 ISR 中的一条指令)。
    2. CPU 检测到 INTR 信号有效。
    3. CPU 向 PIC 发送 INTA (Interrupt Acknowledge) 信号。
    4. PIC 收到 INTA,将当前最高优先级有效请求(即 B)对应的中断向量号放到数据总线上。
    5. CPU 读取中断向量号,保存当前上下文(A 的 ISR 现场),跳转到 B 的 ISR 开始执行。
  • 当前状态: CPU 现在执行 B 的 ISR。
  • 后续可能 (嵌套):
    1. B 的 ISR 开始执行时,它会设置 IMR。由于处理优先级 C > B > A,B 的 ISR 设置的 IMR 会屏蔽 A (A < B),但不会屏蔽 C (C > B)。
    2. 中断源 C 的请求依然有效(它的 IRR 位在服务完成前一直为 1)。
    3. C 的请求通过 IMR(因为 C 优先级 > B)。
    4. PIC 检测到有效的 C 请求,且其优先级高于当前正在执行的 B,因此 PIC 再次将 INTR 置为有效。
    5. CPU 在 B 的 ISR 中执行完一条指令后,再次检测到 INTR 有效。
    6. CPU 再次响应 INTR,发送 INTA。
    7. PIC 将 C 的中断向量号发给 CPU。
    8. CPU 保存 B 的 ISR 现场,跳转到 C 的 ISR 开始执行。
  • 最终状态: CPU 执行 C 的 ISR。此时 C 的 IMR 会屏蔽 B 和 A。C 的 ISR 执行完成后,返回到 B 的 ISR,B 的 ISR 执行完成后,再返回到 A 的 ISR,A 的 ISR 执行完成后,最后返回到最初被 A 中断的主程序。

中断驱动的I/O方式

中断处理过程

  • 中断处理过程中的现场指的是通用寄存器当中的内容
  • 旧屏蔽字就是比如说A的中断被B打断,需要保存当前A的IMR内容,以便下一次执行这个中断源A的中断服务程序的时候,恢复屏蔽字
  • 硬件识别中断不需要查明原因

多重中断的概念

  • 这里的多个中断同时请求是指未被屏蔽的中断
例题

  • ②执行完之后为什么要回到用户程序
    • 因为在二号中断源的环境下,这个中断屏蔽字决定了它是检测不到这个三号中断源发过来的中断请求的。所以就要回到上一层也就是用户程序,只有在用户程序的环境下,才能检测到这个三号中断源发出的中断请求

printf——从系统调用开始

  • 用户通过系统调用命令向操作系统请求内核的服务,系统调用命令进入这个内核态之前,在用户态发起的这个系统调用命令其实就属于用户层I/O软件

  • 操作系统实际上是执行write系统调用来完成printf这个工作
    • printf实际上就是用户要操作系统帮助用户把字符串进行输出,通过write系统调用,所以叫做接口(通过xx来实现某个功能)
  • 普通用户怎么去使用系统调用:可以通过执行C语言中的库函数。

  • 所以叫做库函数里面封装了系统调用,因为printf里面封装了write系统调用
  • 通过int $0x80进入内核态,该条代码以上是用户态,以下是内核态

  • 由操作系统完成也就是由软件来完成的,如果不是由硬件来完成的,就认为是由操作系统来完成的
  • 执行系统调用服务例程其实就是执行中断处理程序

轮询方式下输出一个字符串

  • 状态寄存器是I/O接口里面的一个寄存器
  • 将字符串复制到内核缓冲区,这里的字符串指的是用户缓冲区申请的字符数组char buf,我们首先要将用户缓冲区的内容复制到内核缓冲区当中

  • 任何一个程序都分为用户空间和内核空间,内核空间在真实的物理内存里面,映射到同一个物理内存(内核空间共享)。而他们的用户空间映射的是不同的一个物理内存
  • 因为是在内核态执行程序,所以就需要把用户态的内容复制到内核态,为了数据的一致性和安全性

  • 虽然进入到了内核态了,但是操作系统中,页表中的内容还是原本的进程,里面的内容是能够找到用户缓冲区buffer里面的内容的,所以它可以直接把用户缓冲区当中的内容复制到内核缓冲区的。
    • 这个过程相当于一个访存的指令,在内核态下,它是完全可以访问内存空间的

  • while(printer_status != READY)
    • 假如这个显示器的状态不是就绪状态,就一直循环等待

  • 设备驱动程序来负责一个数据的具体传输
    • 设备驱动程序对设备的I/O端口交互信息,发出命令
    • 等待直到显示器状态为“就绪”就是设备驱动程序完成的
    • 向数据端口输出一个字符是设备驱动程序完成的
    • 发送启动打印命令是设备驱动程序完成的
  • system write的核心功能就是调用这个设备驱动程序来完成实际的字符串的一个输出过程的
  • 整个过程:
    • 在用户态下使用printf库函数
    • printf库函数进入到内核态,发起系统调用,通过system write实现具体的输出功能
    • system write里面调用设备驱动程序drive write,设备驱动程序和I/O端口进行交互

  • 内存缓冲区首址:从内存的哪里开始输出这个字符

I/O接口(I/O控制器)的结构

中断驱动方式下输出一个字符串

  • 中断隐指令中,切换到内核态和保存断点都是硬件完成的
    • 请求和响应之间就是中断隐指令

  • Q1:以单独的形式存在,还是以内核的代码和输出的形式被调用?

  • 轮询方式下,输出n个字符,每个字符都要去向数据端口输出,都要去查询这个设备的状态是否空闲,也就是要重复n次过程
  • 第一个字符的输出完成之后,外设会向CPU发出一个中断请求,然后调出响应的中断服务程序

  • 只有第一个字符由system write完成,其他由中断服务程序完成

  • 由两部分函数完成一个字符串的输出功能
    • system write
    • 中断服务程序
  • 因为是在执行进程B的过程当中响应的中断执行,因此执行的中断服务程序,是在进程B的运行环境里面完成字符的输出
    • 相当于是A本身的任务让别的进程来完成
  • 进程A和中断服务程序都调用了设备驱动程序

  • 进程A的也表里面肯定是有一项包括buffer内容的,但是发生进程切换以后,页表当中的内容变成进程B的内容,如果说不把用户缓冲区当中的内容复制到内核缓冲区,那么发生进程切换之后,进程A的页表内容变成进程B的页表内容,只有用户缓冲区中断服务程序,无法正确地访问缓冲区当中的内容
    • 因此如果复制到内核缓冲区,无论进程如何变化,都共享同一块内核空间,映射是一样的,总能找到内核缓冲区的内容进行正确输出

  • 箭头都是驱动程序干的工作
中断服务程序与设备驱动程序的关系

  • 中断总控程序区调用具体的一个设备驱动程序去完成一个具体的中断处理
  • 计组里面讲的中断处理程序是包括设备驱动程序的
    • 操作系统里面则是抽离出设备驱动程序的具体的数据传输过程

DMA方式下输出一个字符串

  • DMA控制器的总线使用优先级比CPU高是因为DMA控制器当中的数据不被及时取出,很容易被后续的数据冲刷掉,而CPU里面有通用寄存器,可以暂存。
  • 在进行磁盘传输的过程当中,当磁盘寻道结束之后,要向CPU发起一个中断请求信号,执行一个相应的中断服务程序,当DMA控制器执行完这个数据传送之后,也会执行一个中断服务程序,通过DMA结束中断,告知CPU让CPU进行后续处理

  • 周期挪用法、交替使用法、CPU停止法
  • 设备驱动程序是通过静态的内核程序和数据的方式被我们的进程所调用执行的

scanf的完整故事

  • read要读进来一个字符串,应该先放到内核缓冲区,会一直去检查内核缓冲区是否为空
  • 用户缓冲区是scanf函数展开式申请的buffer

  • 从设备控制器中读出数据,将取出的字符送入内核缓冲区,都是设备驱动程序完成的

  • 同步互斥
  • 按下键盘→数据先传送到设备控制器→中断处理程序把数据从设备控制器送到内核缓冲区(具体是设备驱动程序)→scanf发起进程将数据从内核缓冲区送到用户缓冲区

  • 系统缓冲区就是内核缓冲区
  • 用户态到内核态是硬件完成的

期末考试讲解补充

补码运算的本质

  • 进位1就代表2^32,和减去的2^32次方抵消掉了

  • 五段式指令流水线中,EX阶段完成有效地址的计算,M阶段取数,WB阶段写回寄存器
  • [31:20]的第一是把第31-20位取出来送进扩展器
  • DMA方式下CPU所花费的总时间就是预处理和后处理
    • 轮询方式下CPU花费的时间是执行查询程序的时间
    • 中断方式下CPU工作的时间为响应中断请求的时间+中断服务处理的时间

三种I/O方式总结

  • 底下DMA控制方式粗蓝为CPU花费的时间
  • DMA控制方式下从p进程切换到q进程,p进程变为阻塞态,当外设准备好数据之后,p进程从阻塞态变为就绪态,但不一定立即调度,要看具体的调度算法
  • 每传送完一个DMA数据块,DMA控制器就发出一次中断请求
  • 有阻塞才有计算机的并行

取指电路

  • 这里面jump指令利用PC前4位+26位,因为实际上的逻辑地址空间中,内核代码区不允许访问,于是通过限制前4位就可以避免访问,从而jump指令只能在限定范围内转移(内存保护)
  • 因为单周期每个时钟周期完成一条指令,因此不需要写使能信号,由时钟周期控制指令执行,每来一个时钟,PC值就会被更新一次。而在多周期和指令流水线的情况下,不同指令需要的时钟周期不同,更新PC值的时间不固定,因此需要额外的写使能信号

I/O方式

  • 从用户态切换到内核态实际上并没有产生进程切换,只是单纯的一个层次转变,包括中断处理的过程也没有产生进程切换,后面阻塞当前进程然后调度其他进程执行的时候才产生了进程切换
  • 中断不是单独的进程
  • 中断请求信号是控制线发出的
  • 系统调用是自陷
  • 中断服务程序在另一个进程(阻塞后换进来的新进程)的环境下执行,操作系统的页表实际上还是当前另一个进程的

计组强化大题
http://example.com/2025/05/22/计组强化大题/
作者
Kugeln
发布于
2025年5月22日
许可协议