文章目录
- 一、启动:BIOS 与 bootloader
- 中断的处理过程:
- 异常的处理过程
- 系统调用的处理过程
一、启动:BIOS 与 bootloader
从计算机原理的角度来看,最基本的是以下三部分:
其实操作系统一开始并不是放置在内存中的,而是存放在硬盘 disk 上,由 BIOS 【基本输入/输出系统】提供相应的支持,它能在我们按下电源键后,开始检测各种外设,然后加载相应的软件去执行:
如上图所示,BIOS 需要从特定的地址(以 X86 为例,是 Oxf000:fff0,分别表示 CS【段寄存器】和 IP【指令指针寄存器】的值,这两个寄存器合在一起可以形成一个地址的值)开始执行,然后会进行 POST【加电自检】,寻找显卡和执行的 BIOS 。
bootloader 一般放在硬盘的第一个主引导扇区(一般 512 字节),如上图所示,从 OX7C00,往上 512 个字节,放置的是 bootloader 的代码和数据。它能完成的最主要的功能,就是 把 OS 从硬盘加载到主内存中,这样 OS 就能在内存中执行任务了,控制权就交给 OS 了。
操作系统与设备和程序交互 ,主要是 interface 的设计的问题:
(1)操作系统面向外设 是 通过 中断 和 IO 。
(2)操作系统面向应用程序是通过系统调用 和 异常。
🖖先来看看三者的粗糙😌概念:
(1)中断(来源于外设)
- 来自不同的硬件设备的计时器和网络的中断。
(2)异常(来源于不良的应用程序)
- 非法指令或者其他坏的处理状态。
(3)系统调用(来源自应用程序)
- 应用程序 主动 向操作系统发出服务请求。
🤙为什么需要中断、异常和系统调用
为什么应用程序不能直接去访问外设,而是要通过操作系统?因为在计算机运行中,(操作系统)内核是被信任的第三方;只有内核可以执行特权指令;为了方便应用程序,更具有通用性、可移植性。
中断、系统调用、异常的区别:
(异步:操作系统不知道哪时候产生中断。而异常和系统调用,是在特定指令执行后才会产生的,这样的话是同步的。而对于系统调用,发出请求的点是同步的,而返回的点可能是异步的,也可能是同步的。 ❓❓❓没咋懂。)
中断的处理过程:
产生中断后,需要知道中断是由哪个服务例程进行服务,因此,需要建立一个 表,📃 key是中断号,(比如键盘和鼠标对应的中断号就不同) ,value 是服务例程对应的地址。
- 硬件:
在 CPU 初始化时设置中断使能标志,依据内部或外部事件设置中断标志,依据中断向量调用相应中断服务例程。 - 软件(操作系统):
(1)编译器保存被打断的执行现场
(2)根据中断号查询到对应的服务例程的地址,跳转到该地址执行。
(3)清除中断标记(服务例程)
(4)编译器恢复之前保存的处理状态
(应用程序感知不到有中断产生。)
异常的处理过程
应用程序执行某个特定的指令之后,这个指令触发了异常事件,比如除以零操作,CPU会得到一个异常的 ID 编号,首先需要保存产生异常的执行现场、产生异常的指令、该指令的地址 以及 当前寄存器的内容,然后操作系统会根据异常的编号做相应的处理(可能是退出执行,也可能是弥补服务工作然后恢复,让应用程序重新执行某个指令)。
系统调用的处理过程
系统调用是操作系统服务的编程接口,通常由高级语言编写(C或者C++),程序访问通常是通过高层次的API接口而不是直接进行系统调用。
- 三种最常用的应用程序编程接口(API):
(1)Win32 API 用于 Windows;
(2)POSIX API 用于 POSIX-based systems (包括UNIX,LINUX,Mac OS X的所有版本);
(3)Java API 用于JAVA虚拟机(JVM) - 如图所示,应用程序 会直接或间接地通过 函数库 访问系统调用接口,然后就会触发 从用户态【应用程序在执行的过程中,CPU 处于的一个特权级状态,特权级特别地低,不能够直接访问某些机器指令和直接访问 IO】 到 内核态 【操作系统运行的过程中,CPU 处于的状态,操作系统可以执行任意一条指令,这时可以完全控制整个计算机系统】 的转换。从而使得控制权从应用程序交给了操作系统,操作系统可以对应用程序发出的参数 ID 做一个标识,然后就可以进行识别,进而完成具体的服务。
- 系统调用和普通的函数调用的区别:
当应用程序发出函数调用时,是在一个栈 完成了参数的传递和返回;而系统调用,应用程序、内核 ,是具有各自的堆栈,而且需要完成从用户态到内核态的转换,系统调用的开销更大,不过更安全可靠。 - 中断、异常、系统调用 的处理过程,以及与应用程序、外设交互的过程,是跨越了操作系统和应用程序、操作系统和外设的边界的,跨越边界的代价:
(1)在执行时间上的开销超过程序调用的开销。
(2)开销
① 建立中断/异常/系统调用 号 与 对应服务例程 映射关系的初始化开销 (是个表。)
② 建立内核堆栈 (操作系统的堆栈不能和应用程序的堆栈混为一谈。)
③ 验证参数 (操作系统不信任应用程序,所以为了安全起见。)
④ 内核态映射到用户态的地址空间
⑤ 内核态独立地址空间