文章目录
- 0. 内容摘要
- 1. 计算机体系机构概述
- 2.启动
- 2.1 启动时计算机内存和磁盘布局
- 2.2. 内存映射
- 3. 系统调用、异常、中断
- 3.1 定义
- 3.2 背景
- 3.3 中断、异常和系统调用的不同点
- 3.3.1 源头
- 3.3.2 处理时间
- 3.3.3 响应
0. 内容摘要
两部分的内容
- 第一部分是启动。知道操作系统怎么是从上电之后,启动到正常运行,然后最终能够让应用程序能够正常地去开始它们各自的工作,这是一个关于启动这一块。
- 另外一部分是中断、异常和系统调用。需要去了解操作系统是如何提供相应的接口来给应用提供服务,以及来控制外设或和外设进行交互。那么这里面涉及到一系列的一些基本概念,包括中断、异常和系统调用。那为此会对这三部分会分别讲解它的一些背景,然后它的一些特征以及如何去实现在操作系统里面去如何去实现中断、异常和系统调用。
1. 计算机体系机构概述
首先看一下启动,那么大家都知道计算机启动之后,好像机器自动就可以干很多事情,那其实如果是从计算机原理的角度来看,那最基本的就三部分,一个是 CPU,一个是 memory,一个是 IO。
2.启动
2.1 启动时计算机内存和磁盘布局
其实操作系统一开始并没有放在内存里面,直接可以让CPU 去执行,它其实是放在我们的 DISK 硬盘上面,然后由 bios 来提供相应的支持。
那 bios 什么东西?它的全称是基本 IO 处理系统,这个处理系统主要的功能就是计算机开机之后,就能够让计算机系统开始检测各种各样的外设,这是 bios 完成最基本的功能。
bios 检测完外设之后,才去加载相应的软件来进行执行。在地址上面,OS 是放在 DISK 上面的,其实除了 OS 之外,还有一个很简单的小程序叫 Bootloader。
Bootloader 的主要的功能是负责加载 OS,能够让 OS 从硬盘放到内存里面去,然后让 CPU 可以执行操作系统。
另一方面可以看到,看这幅图,在整个计算机内存里面,它有一部分空间被 bios 已经占满,已经预先占上,但是还有很多地方是空的,那实际上 bios 第一步要干的事情,它需要干的是从一个特定地址开始执行,如果以 X86 为例的话,这个地址是固定的地址,在0xf000 : fff0 ,分别代表 CS 和 IP 这两个寄存器的值。
CS 是 段寄存器,IP 是指令寄存器,这两寄存器值合在一起,可以形成一个具体的内存的地址,那么一开始加电,那我们这个 bios 就从这个地址开始执行。
这个地址执行之后,会完成接下来一系列工作,包括自检 ,检查自身的各种各样的设备是否能够正常工作。
有哪些基本设备?
- 其实计算机能够给应用程序正常运行,需要屏幕,屏幕能够反映出来各种各样的图像和文字,可以来展示各种各样功能,所以说实际上是有个显卡显示驱动在执行。
- 另一方面,它有键盘、鼠标,便于去控制相应操作。
- 以及还把数据存储存在DISK 上面。
这些最基本的操作,都是要由 bios 来负责进行初始化的检查。假定它检查完毕之后发觉,这些外设都是能够正常工作的,那么它接下来干的事情就是需要去把 Bootloader 从硬盘上放到内存中去,这是 bios 完成的最基本的一些功能。
bios 是如何把Bootloader 放进去的?
2.2. 内存映射
看这幅图就可以看出来,Bootloader 是放在硬盘上,放在硬盘什么地方?这点其实也是一个很特殊的设置,Bootloader 一般是放在第一个硬盘的第一个主引导扇区,这样的话 bios 是很容易找,只要找到硬盘的第一个扇区,就可以把它加载到内存中去,那这是它的一个最基本过程。
一般硬盘的第一个扇区就512 个字节,那也意味着 Bootloader 仅仅只有512个字节,这么小的区间就可以完成很多有趣的功能。其中最主要的功能就是接下来要把一个更复杂的软件,OS 从硬盘中给放到内存中去,这是 bios 加载 Bootloader ,Bootloader 在负责加载 OS 的大致过程。
以X86为例,Bootloader 会放在内存中什么地方呢?这一点是 bios 具体设定好的,比如它放在0x7C00地址,然后连续从这一直往上 512 个字节,就是 Bootloader 的主要代码和数据。
这个小的代码和数据其实能完成的功能也很有限,所谓的功能有限是说,它只是完成了把接下来的操作系统的代码和数据从硬盘中加载到内存中去。看上图右边可以看出来,当 bios 把 Bootloader 从硬盘放到内存中去之后,CPU 的控制权就已经由 Bootloader 来掌握了。
那 Bootloader 接下来需要干些什么呢?很明显,第一步它需要去找到硬盘的起始扇区,以及硬盘上操作系统的起始扇区,以及这个操系统的长度,然后把这些磁盘块,从硬盘中读到内存中去,这是 Bootloader 完成最主要的工作。
读到内存中之后,按道理它的工作就完成了,它工作完成之后,它会把 CPU 的控权交给 OS,实际上是跳到 OS 的起始地址去执行,这样OS 就可以在内存中进行它所必要的工作,从前期的初始化工作到后期的能够创建各种各样的应用程序上去运行,那到这个阶段,实际上所有的管理,整个计算机系统的硬件的管理都已经处于 OS 的管理之下。
上述这个过程,实际就是 Bootloader 的启动过程。
3. 系统调用、异常、中断
第一步就简单地把计算机系统的启动以及操作系统怎么一个启动过程给大家做个简单的介绍,那接下来介绍一下,当操作系统正常工作之后,它需要如何与外设和应用程序打交道,这里面实际上是有 interface 的一个设计问题,那操作系统的 interface 是什么呢?包含三个:面向外设是通过中断和 IO 来进行处理, 面向应用程序是通过系统调用和异常来提供相应的一些功能。
所以说有必要需要对这三个基本机制有一定的了解,才能够知道操作系统怎么来给外设进行管理和控制,怎么来给应用程序提供相应的服务和支持。这是接下来要介绍的关于中断、异常和系统调用。
3.1 定义
- 系统调用就是应用程序主动向操作系统发出服务请求,希望操作系统能够提供相应的支持。
发出了一个服务请求,这个服务请求是由应用程序主动向 OS 提出,所以说系统调用就是由应用程序向操作系统发出的一条特殊的一条指令,让操作系统来完成相应功能,这叫系统调用。
-
异常和系统调用不太一样,异常也是由应用程序产生的,但是它并不是应用程序主动想产生异常,实际上是应用程序在执行过程中出现了一些意想不到的事情。使得不得不由操作系统来完成相应的功能,这是异常。
-
中断和前两者又不一样,前两者来源于应用程序,中断是来源于外设,外设有些特殊事情需要操作系统去提供相应的支持,那这时候通过这个中断机制,让操作系统能够感知到有外设来发出请求了,需要处理,这个过程称为中断。
3.2 背景
为什么应用程序不能直接去访问外设呢?为什么通过操作系统?这是很容易想到的问题
- 其实在一开始讲这个操作系统基本介绍的时候提到过,操作系统是一个特殊的一段软件,它和应用程序最大的不同在于,它具有对整个计算机系统的一个控制权,它能够执行特权指令,它是一个可信任软件。
它不像应用程序,应用程序实际不可信任,有可能有些恶意程序会对整个系统破坏,但是信任操作系统可以提供一个安全的服务,所以说应用程序不能直接去访问外设,因为直接访问外设很容易造成整个计算机系统崩溃,这是安全角度。
-
另一方面。希望通过操作系统能够给上层的应用提供一个更简单一致的接口,使得上层应用不用去关注底下这些 device 的不同的这些细节,来针对不同 device 开发不同的软件,这样实际效率很低,所以说希望通过操作系统能够屏蔽底层这个 device 的复杂性和差异性,给上层提供了一个很简单很简洁的接口。使得应用程序写得更加通用,可移植。
基于这两点,实际才设计出这三个基本的概念。
其实还关注,当操作系统在处理这三种事情时候,到底操作系统需要注意哪些事情,应用程序需要注意哪些事情。它们和通常说的,比如说既然有系统调用和函数调用,它们有什么区别?最终理解在操用系统里面这三者它们的特点是什么,然后以及操作系统如何去考虑去设计和实现对这三类机制的支持。
3.3 中断、异常和系统调用的不同点
中断、异常和系统调用之间到底有什么样的特点和差异。上边这幅图比较抽象地展示出来了这三者在实现和处理的大致过程。
3.3.1 源头
首先从源头来看,中断、异常和系统调用它们三者产生的源头是有不同的。
- 首先看中断,中断很明显来源于外设,它并不是由 CPU 产生,而是由外设产生。这些外设都可以产生各种各样事件,让操作系统进行处理,那这称之为中断事件,它的来源是来源外设的。
外设包含很多种,比如键盘会产生键盘敲击字符的事件,鼠标会产生移动事件,还有网卡产生网络包事件,还比如声卡、显卡等等
- 异常比较特殊,它并不是应用程序主动想去产生的事件,而是应用程序在执行某个特定代码的时候发生一些异常意想不到的事情。为满足这些异常事件,促使操作系统去更好地给应用程序服务,所有这些异常其实不是应用程序主动想去发出的请求,而是一种让操作系统去应对一些意外事件的支持,这称为异常。
比如说程序在执行过程中可能做了除零操作,它其实并没有意识到不能做除零操作,这是让计算机系统不能正常工作的指令,为此操作系统需要能够发现这种情况,然后及时地处理,这是一种情况。
~
另外有可能有些恶意的程序,由于某种原因它需要越过它的权限去访问另外程序的地址空间,这地空间是受保护的,那它这种操作应该能够及时地被操作系统截获,这也是一种异常。
~
甚至还有一情况是说,当应用程序在执行过程中,它需要的资源没有得到满足,这时候操作系统悄悄地在后端把这个事情给处理好,然后让我们应用程序再去执行。
- 系统调用和异常表面看来源是一样,都从应用程序产生的,但是它是主动要求很明确指出需要操作系统提供什么服务。这个服务是主动、有意识产生的服务请求,所以它有一个很明确的指令和相应参数来说明,到底需要什么样的请求。
比如打开文件,关闭文件,读写文件,或者发送网络包等等,这些都是系统调用。这些系统调用都是由操作系统来具体完成,应用程序只需按照定义好的系统调用接口,把接口参数设置好之后,具体实现是由操作系统来完成。操作系统完成之后,应用程序得到返回结果,继续去做它该做的事情。
3.3.2 处理时间
- 中断一般称为异步事件,异步意思说当这个事件产生的时候,应用程序并不知道什么时候会产生,这就是异步,它不知道什么时候会产生。
- 异常比较明确,一定是异常执行到某条特定的指令之后一定会产生,比如执行除零指令,它一定会产生异常。
- 系统调用比较明确,执行系统调用,在特定指令触发了系统调用请求,让操作系统来完成,这个指令的位置是一个同步的时间点。
所以说中断是异步时间点,异常和系统调用是同步时间点,这是所谓同步异步的定义。
注意 :系统调用有可能也是异步的
异步体现在另一个特点上面来说,当系统调用发出请求之后,它的返回时间是异步的,正常情况下返回是同步的,当系统调用发出之后,比如要读一块数据,发出这个请求之后,操作系统就应该去完成这个读数据的处理,最终把数据返回系统调用,在这过程中应用程序处于等待状态,那这个返回是一个同步的过程。但如果换个角度来说,如果说应用程序发出请求之后,马上去做其他事情了,那操作系统完成读数据操作之后,会给应用程序发出一个异步消息,说这事情做完了,那实际返回的过程是一种异步执行过程。
从中可以看出来,对于系统调用而言,它发出请求时间点是同步的,但是它返回点有可能是异步的,也可能是同步的,这就是它和中断和异常不太一样的。
3.3.3 响应
- 中断打断了当前应用程序正常执行,但是应用程序其实并没有感觉到有中断产生,为什么呢?因为操作系统把这过程给悄悄地完成了,它并没有去影响应用程序,所以应用程序在执行过程中,其实感觉不到时不时地产生中断,那中断过程完全是由操作系统透明地完成,对应用程序是透明的。
- 但是异常不一样,一旦产生异常有可能导致严重后果,把产生异常的应用程序给杀死,让它退出,这是严重情况。那么也有一种情况,让产生异常的应用程序重新执行产生异常的指令,因为操作系统提供相应一些支持之后,有可能再次执行的时候,这条产生异常的指令可以正常执行下去。
- 对系统调用而言,它的处理过程基本上是等待服务完成之后,它就继续执行了,它不会重复执行产生这个系统调用的指令。
可以看出这三者确实都有各自不同的地方。