理解计算机系统_异常控制流(一):异常

前言
       

        以<深入理解计算机系统>(以下称“本书”)内容为基础,对程序的整个过程进行梳理。本书内容对整个计算机系统做了系统性导引,每部分内容都是单独的一门课.学习深度根据自己需要来定

引入 

        异常控制流这章实际上是操作系统的一部分.操作系统简单的被描述为:虚化CPU,虚化内存,进程管理,文件管理这几部分内容.核心是进程管理,其他几部分内容都是围绕着他来进行的.

        为什么说进程管理是操作系统的核心,操作系统是一个用CPU指令编写的软件,用于管理多任务系统.开始时计算机设计出来,只能做单个任务,像现在的单片机就是做单个任务或者极少数任务的,他没有操作系统,任务都是一个个来.当计算机要处理多个任务时,就会涉及多个任务之间的切换,资源共享,数据保护,外部硬件IO交互等问题,操作系统就是作系统资源调度,他最核心的部分就是怎样进行进程的管理.

        操作系统是计算机软件中一项伟大的发明,是作为基础软件存在于各种"高级"电子设备当中的,比如桌面电脑,服务器,手机,企业端等等.本章内容和前面几章一样,学起来有些抽象,还是那个问题,你只知道怎么用,却不知道是怎么来的.但比起前面几章的内容完全面对汇编语言,操作系统很大部分是C语言写的,看起来"亲切"很多,也比较容易理解.操作系统内容丰富,其中有不少值得思考的细节.如果想做一个优秀的操作系统工程师,那么应该深究.

        本章概念很多,把概念理解清楚,对学习有很大帮助.

        本贴内容包括本书第8章异常控制流第1节:异常

异常控制流ECF概述

        异常控制流概念的由来,先说说什么是"正常控制流" .代码经过编译后,程序计数器PC会按照指令序列一个个去依次执行.这种情形就叫做正常流在运作.""可以看作一段编译后的代码或者程序.正常流是一个"平滑"的序列,没有跳转,调用和返回这样的指令.当然代码中的函数基本上都会有调用其他函数,所以正常流只能被看作是一个最基本的单元.

        异常控制流和正常流的区别在于现代系统通过使控制流发生突变来对这些情况做出反应.(黑体字是本书原话).本书列举了几种情形---硬件中断,进程间的切换,信号可以造成异常控制流.他的结果是CPU必须对产生的异常控制流进行处理.

异常基础

        从字面意思上说:异常表示错误或者故障.但这里的异常不完全指这种情况,还包括了与操作系统交互的手段---陷入内核.

        1>事件的概念

        在处理器中,状态被编码为不同的位和信号.状态变化称为事件(event).--(黑体字是原话)

        ----解读:CPU有个专门监测状态的寄存器,每隔一段时间扫描(当然这个时间不会太长,可能是微秒级或者毫秒级),当得到某个数字时,即产生某种异常.(笔者不知道状态寄存器具体实现是怎样的,但可以作以下假设):用n位寄存器,可以记录2ˆ-1个状态(加上无异常产生的1个).所以如果表示255种异常,用8位寄存器就够了.

        当状态发生变化,表示产生了事件.在任何情况下,当处理器检测到有事件发生时,它就会通过一张叫做异常表(exception table)的跳转表,进行一个间接过程调用(异常),到一个专门设计用来处理这类事件的操作系统子程序(异常处理程序)--(黑体字是原话)

        ----解读:事件产生后,CPU马上查询异常表,知道产生哪一种异常后,开始执行相应异常处理程序

        思考:异常是怎样采集的?

        异常可能由硬件产生,或者操作系统出现某些状况,外部I/O设备给出信号等等情况.异常出现后,被操作系统内核中一个内存位置描述,CPU寄存器与操作系统内核交互,CPU扫描后可得.如图所示

        这个CPU寄存器就是上面所说专门用于监测状态的寄存器.CPU可以由他的值得知是否发生了事件,并根据传进来的异常号知道发生了哪种事件.

        触发异常的条件需要注意,如果是必须马上处理的,应该作为异常,如果不一定需要马上处理,应该放在普通代码里执行,程序里用if-else去编写.

        2>异常号

        系统中可能的每种类型的异常都分配了一个唯一的非负整数的异常号(exception number).其中一些号码是由处理器的设计者分配的,其他号码是由操作系统内核(操作系统常驻内存的部分)的设计者分配的.前者的示例包括被零除、缺页、内存访问违例、断点、以及算数运算溢出.后者的示例包括系统调用和来自外部I/O设备的信号.--(黑体字是原话)

        ----解读:异常号的作用:提出需求,怎样表达异常,如下图

异常号示意(非原表)
异常号表达含义CPU或操作系统
0被零除CPU
1缺页CPU
2非法内存访问CPU
3系统调用操作系统

        设计者要提出这样的一张表,用1个异常号描述1个异常. 

        这里有一个非常重要的概念:操作系统内核.他有两个特征,一是常驻内存,所以通常所说的内存被操作系统占据了,内核作为操作系统的一部分,内存部分给了他.二是内核可以和CPU寄存器交互.前面分析程序运行时,有move,leaq等指令把内存数据赋值给寄存器,或者让寄存器指向某个内存位置.推导出CPU寄存器可以访问某些内存位置,并执行一些操作(具体什么操作以及怎么实现的,要设计人员才知道,不必深究,会给使用者留出接口--这也是反复提到的:你只知道怎么用,不知道怎么来的).

        3>异常表

        在系统启动时(当计算机重启或者加电时),操作系统分配和初始化一张称为异常表的跳转表,使得表目k包含异常k的处理程序的地址.--(黑体字是原话)

        ----解读:如前面分析,异常表是放在操作系统内核中的一张表,能被CPU寄存器访问.

        1)思考异常表的设计.如果有新的异常出现,怎样在异常表上反映.

        一是更改条目,新增异常号,本书图8-2异常表中有n个异常,新增为n+1.添加相应的异常处理代码.

        二是不用更改条目,将新异常归于某个已存在异常中,在对应的异常处理代码中加上if,else分支进行说明.

        因为异常表是操作系统内核提供的,随时可以更新,所以不用考虑太多,上述只提供一种思路.异常表的两端--异常号和处理异常的代码是"开放式"的,可以在新增异常情况下不更改现有条目---这点和面向对象中的"接口"有一点类似.

        以下黑体字是本书原话:在运行时(当系统在执行某个程序时),处理器检测到发生了一个事件,并且确定了相应的异常号k.随后,处理器触发异常,方法是执行间接过程调用,通过异常表的表目k,转到相应处理程序.

         图8-3展示了处理器如何使用异常表来形成适当的异常处理程序的地址.异常号是到异常表的索引,异常表的起始地址放在一个叫做异常表基址寄存器的特殊CPU寄存器里.

        注意:截图中有错误,异常号(x84),本书纸质版中是x4, 表示两个地址间差距为4,所以异常表中是32位地址(类似0x12345678).异常表前面的0,1表示异常号

        2)异常表的实现

        异常表的实现和PC(程序计数器)类似,又有所区别.为了说明怎么工作的,作以下假设:

        异常表起始地址为0x12345678,异常处理程序0的代码起始地址0xAAAAAAAA,异常处理程序0的代码起始地址0xBBBBBBBB.给异常表基址寄存器取个名字%base_exception.

                1.把处理代码首地址放在异常表中

leaq 0xAAAAAAAA,0x12345678    //异常表首个元素地址指向异常代码0
leaq 0xBBBBBBBB,0x1234567c    //异常表第2个元素地址指向异常代码1
...                           //以此类推
...                           //以此类推

                2.异常表基址寄存器指向异常表

​
leaq 0x12345678,%base_exception    //异常表基址寄存器指向异常表

                当异常发生,CPU接收到异常号时,需要调用相应的异常处理程序.因为不清楚是怎么执行的,所以作以下假设:

                1]异常号放在一个叫做%number_exception的寄存器中

                2]把异常代码的首地址交给一个叫做%run_exception的寄存器,即开始执行异常处理程序.

//以下伪代码
MUL 4,%number_exception                   //将异常号寄存器中的数值乘以4并放回
addq %number_exception,%base_exception    //计算出异常基址寄存器应该指向的地址
move (%base_excetion),%run_exception      //将取得的异常代码首地址传给管运行的寄存器

        PC(程序计数器)和异常表基址寄存器执行的区别如下:

        PC执行指令的过程,取得一条指令,由CPU执行.完毕后PC地址加1,取得下一条指令,再交由CPU执行.而异常表基址寄存器执行指令的过程是取得一条指令就执行一条.

========================内容分割线=========================================

        题外话:书本上没有相关知识,靠自己反推再次显得很"窘迫",关键还是思路.

        PC执行指令的过程中,有没有一个表示指令开始执行的寄存器,笔者也不知道.所以执行异常代码的机制属于笔者假设.

        上述过程1和2部分是没有问题的.异常表实现的前提是实现两个指针关系:

        1>异常表基址寄存器指向异常表首元素

        2>异常表每个元素指向某个异常处理代码的首地址

        此外还有个地方,异常表首元素的地址是否固定(上述代码中假设他是0x12345678),这个也是不确定的.因为异常表是操作系统加载的,也可能是浮动地址.如果是浮动地址(假设为A),那么在实现2>的时候,应该把他替换为浮动地址A,后面相应的指针关系做改变,先计算出地址,再进行指针,如下所示

leaq 0xAAAAAAAA,A             //异常表首个元素地址A指向异常代码0
ADD  4,A                      //32位地址加4
leaq 0xBBBBBBBB,A             //异常表第2个元素地址指向异常代码1

         回顾指针:指向某个地址的指针,可访问被指向地址开始的连续数据

========================内容分割线=========================================

        3)异常表和调用的不同

         本书P503:过程调用中,在跳转到处理程序之前,处理器将返回地址压入栈中.然而,根据异常的类型,返回地址要么是当前指令(当事件发生是正在执行的指令),要么是下一条指令(如果事件不发生,将会在当前指令后执行的指令).--黑体字是原话

        ----解读:红色部分的返回地址指的是"指令地址",栈指的是"指令栈".(本书前面讲的"栈帧",是在数据寄存器需要被使用时才产生的.这里的"栈"也是一样).因为本书没有特别说明指令---PC程序计数器的工作,所以加上指令栈这一点.

        当PC顺序执行被异常中断后,他当前的指令地址或者他将要执行的下一条指令地址被压入指令栈中,根据异常类型而定.

异常的类型

        异常分为4类:中断,陷阱,故障和终止

        其中中断是异步发生的,陷阱,故障和终止是同步发生的,是执行当前指令的结果.

        1>中断

        本书P504,以下黑体字是原话:I/O设备,例如网络适配器、磁盘控制器和定时器芯片,通过向处理器芯片上的一个引脚发信号,并将异常号放在系统总线上,来触发中断,这个异常号标识了引起中断的设备.

      ----解读:从这段话可以做出以下分析

        1)硬件中断是由CPU直接采集的异常.那么又是如何实现的呢?CPU难道专门拿一根引脚来响应硬件中断,其他都不做吗?笔者认为显然是不会的.不然那些64位的指令又如何去完成呢?在执行完一条指令后,如果CPU扫描到这个引脚的高电平,并根据操作系统内核交上来的异常号,去执行对应的异常处理代码.

        再来分析一下这个异常号:异常号是设备提交给操作系统,操作系统做批准,CPU执行对应代码.因为设备提交的异常号可能已被占用,操作系统必须根据实际情况决定分配给设备的异常号是多少.

        对于CPU而言只是被动执行.CPU好像在说:"我知道你的事做完了,请告诉我下一步要做什么?"在异常表中,异常号表示异常基础寄存器的偏移(x4),找到指令交由CPU执行.操作系统也不知道异常处理代码该怎么写,所以异常处理代码应该由使用硬件设备的人设计并提供给操作系统.

        此处1台设备是否能提交几个异常号,也就是1台设备设置几种中断?根据实际情况来定.或者没有必要.只要是设备发出中断请求,一律停止该设备运行.---这是一种简单的处理方法.

        综上所述,异常号其实是设备编号.

          2)在硬件的角度,他倒是应该用一个专门的引脚发出中断请求.比如网络适配器在网络信号发送完毕后,有一个引脚一直处于高电平,这样CPU就能注意到了.

        此外,每个硬件的驱动程序里,应该向操作系统提交异常号,和相应的异常处理代码.在发生某种状况后,接下来的内容是什么,和1)相对应.做个设想:在网络数据包发送过程中,突发情况需要停止传数据,这时应由CPU执行异常处理代码终止数据发送.

        注意:以上分析属于发散思考,不保证对错.

        2>陷阱和系统调用

        陷阱是有意的异常,是执行一条指令的结果.就像中断处理程序一样,陷阱处理程序将控制返回到下一条指令.陷阱最重要的用途是在用户程序和内核之间提供一个像过程一样的接口,叫做系统调用.---黑体字是原话

        3>故障和终止

        故障和终止都是由错误引起的.其中故障可能被修复,修复完毕后执行下一条指令.否则调用abort终止引起故障的程序;终止是无法修复的错误,直接调用abort终止.

linux系统调用

        

小结

        异常的一些分析 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

驱动学习(三)符号导出

1.什么是符号&#xff1f; 主要是指全局变量和函数 2.为什么要导出符号&#xff1f; ​ linux内核采用的是模块化的形式管理内核代码。内核中每个模块之间是相互独立的&#xff0c;也就是说A模块的全局变量和函数&#xff0c;B模块是无法访问的。若B模块想要使用A模块中的已有…

python爬虫——Selenium的基本使用

目录 一、Selenium的介绍 二、环境准备 1.安装Selenium 2.安装WebDriver 三、元素定位 1.常用定位元素的方法 2. 通过指定方式定位元素 四、窗口操作 1.最大化浏览器窗口 2.设置浏览器窗口大小 3.切换窗口或标签页 切换回主窗口 4. 关闭窗口 关闭当前窗口 关闭所…

java_方法重载、可变参数、作用域

方法重载 基本介绍 java 中允许同一个类中&#xff0c;多个同名方法的存在&#xff0c;但要求 形参列表不一致&#xff01; 比如&#xff1a;System.out.println(); out 是 PrintStream 类型 重载的好处 减轻了起名的麻烦减轻了记名的麻烦 案例 public class OverLoad01 …

SCI一区级 | Matlab实现SSA-TCN-LSTM-Attention多变量时间序列预测

目录 效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.基于SSA-TCN-LSTM-Attention麻雀搜索算法优化时间卷积长短期记忆神经网络融合注意力机制多变量时间序列预测&#xff0c;要求Matlab2023版以上&#xff0c;自注意力机制&#xff0c;一键单头注意力机制替换成多头注…

leetcode刷题(76-80)

算法是码农的基本功&#xff0c;也是各个大厂必考察的重点&#xff0c;让我们一起坚持写题吧。 遇事不决&#xff0c;可问春风&#xff0c;春风不语&#xff0c;即是本心。 我们在我们能力范围内&#xff0c;做好我们该做的事&#xff0c;然后相信一切都事最好的安排就可以啦…

深度生成模型 - 受限玻尔兹曼机(RBM)篇

前言 受限玻尔兹曼机&#xff08; Restricted Boltzmann Machine&#xff0c;RBM \text{Restricted Boltzmann Machine&#xff0c;RBM} Restricted Boltzmann Machine&#xff0c;RBM&#xff09;是深度学习领域中的一种重要模型&#xff0c;其起源于统计物理学&#xff0c;由…

【再谈设计模式】单例模式~唯一性的守护者

一、引言 在软件工程中&#xff0c;软件开发&#xff0c;设计模式是提高代码复用性和可维护性的有效工具。单例模式&#xff08;Singleton Pattern&#xff09;作为一种创建型设计模式&#xff0c;旨在确保一个类只有一个实例&#xff0c;并提供对该实例的全局访问。这一模式在…

如何在 Elasticsearch Ruby 客户端中使用 ES|QL Helper

作者&#xff1a;来自 Elastic Fernando Briano 了解如何使用 Elasticsearch Ruby 客户端编写 ES|QL 查询并处理其结果。 简介 Elasticsearch Ruby 客户端可用于编写 EQ|QL 查询&#xff0c;使处理从 esql.query 返回的数据更加容易。ES|QL 允许开发人员通过查询过滤、转换和分…

redis详细教程(3.ZSet,Bitmap,HyperLogLog)

ZSet Redis 的 ZSet&#xff08;有序集合&#xff09;是一种特殊的数据类型&#xff0c;它允许存储一系列不重复的字符串元素&#xff0c;并为每个元素关联一个分数&#xff08;score&#xff09;。这个分数用于对集合中的元素进行排序。ZSet 的特点是&#xff1a; 唯一性&am…

MYSQL-SQL-03-DQL(Data Query Language,数据查询语言)(单表查询)

DQL&#xff08;数据查询语言&#xff09; DQL英文全称是Data Query Language(数据查询语言)&#xff0c;数据查询语言&#xff0c;用来查询数据库中表的记录。 查询关键字: SELECT 在一个正常的业务系统中&#xff0c;查询操作的频次是要远高于增删改的&#xff0c;当我们去访…

Cisco Packet Tracer 8.0 路由器的基本配置和Telnet设置

文章目录 构建拓扑图配置IP地址配置路由器命令说明测试效果 构建拓扑图 1&#xff0c;添加2811路由器。 2&#xff0c;添加pc0。 3&#xff0c;使用交叉线连接路由器和pc&#xff08;注意线路端口&#xff09;。 4&#xff0c;使用配置线连接路由器和pc&#xff08;注意线路…

优化网站结构提升用户体验的关键要素

内容概要 在数字时代&#xff0c;网站的架构和用户体验密切相关。一个合理的网站结构不仅能帮助用户快速找到所需信息&#xff0c;还能提升整体的访问满意度。为了达到这一目的&#xff0c;网站需要强调几个关键要素。 首先&#xff0c;清晰的导航设计至关重要。导航应当直观…

Android Gradle

#1024程序员节&#xff5c;征文# Gradle 是一款强大的自动化构建工具&#xff0c;广泛应用于 Android 应用开发。它通过灵活的配置和丰富的插件系统&#xff0c;为项目构建提供了极大的便利。本文只是简单的介绍 Gradle 在 Android 开发中的使用&#xff0c;包括其核心概念、构…

微积分复习笔记 Calculus Volume 1 - 3.8 Implicit Differentiation

3.8 Implicit Differentiation - Calculus Volume 1 | OpenStax

Java——lambda表达式和StreamAPI

一、lambda 1. lambda表达式 1.1 Lambda表达式的使用举例: (o1,02)->Integer.compare(o1,o2); 1.2 Lambda表达式的格式举例: Lambda形参列表->lambda 1.3 Lambda表达式的格式 lambda操作符或箭头操作符 的左边:lambda形参列表&#xff0c;对应着要重写的接口中的…

django游戏门户系统

想做毕业设计但还没有头绪&#xff1f;&#x1f64b;‍♂️django游戏门户系统了解一下&#xff01;这个系统不仅功能全面&#xff0c;还能轻松解决你的项目选题难题&#xff01; 我们这个基于Django开发的游戏门户系统提供了用户注册、登录、内容发布以及管理功能&#xff0c…

大数据日志处理框架ELK方案

介绍应用场景大数据ELK日志框架安装部署 一&#xff0c;介绍 大数据日志处理框架ELK&#xff08;Elasticsearch、Logstash、Kibana&#xff09;是一套完整的日志集中处理方案&#xff0c;以下是对其的详细介绍&#xff1a; 一、Elasticsearch&#xff08;ES&#xff09; 基本…

【SQL实验】表的更新和简单查询

完整代码在文章末尾 在上次实验创建的educ数据库基础上&#xff0c;用SQL语句为student表、course表和sc表中添加以下记录 【SQL实验】数据库、表、模式的SQL语句操作_创建一个名为educ数据库,要求如下: (下面三个表中属性的数据类型需要自己设计合适-CSDN博客在这篇博文中已经…

LeetCode反转链表

题目描述 给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5] 输出&#xff1a;[5,4,3,2,1] 示例 2&#xff1a; 输入&#xff1a;head [1,2] 输出&#xff1a;[2,1] 示例 3&#…

011:软件卸载工具TotalUninstall安装教程

摘要&#xff1a;本文详细介绍软件卸载工具TotalUninstall安装流程。 一、软件介绍 TotalUninstall是一款功能强大的卸载与清理工具&#xff0c;它能够彻底卸载不需要的应用程序&#xff0c;并清除相关的注册表项、文件残留和临时文件&#xff0c;确保系统干净无残留&#xff…