系列文章目录
文章目录
前言
参照B站上面的操作系统视频所写的笔记
我看这门课程的视频是为了补充自己在基础知识上的不足,所以并没有做实验。
另外,在写笔记的过程中,也搬运了一些其他博客的内容,有些地方没有标注来源,如果原创作者觉得有任何问题都可以联系我,侵删。
一、概述
1、 什么是操作系统
- 从用户、控制的角度:操作系统是个控制软件,管理应用程序、为应用程序提供服务、杀死应用程序。
- 从资源分配的角度来说:
- 操作系统进行资源管理、管理各种外设、对资源进行分配。
- 操作系统将物力资源进行抽象:CPU抽象成进程、磁盘抽象成文件、内存抽象成地址空间提供给应用程序使用。
2、操作系统层次结构
位于硬件之上,应用程序之下。
是个中间层的系统软件。
操作系统位于应用软件之下、为应用软件提供服务支撑,完成对硬件的管理。
操作系统有两层对外的接口:对外暴露的接口外壳(Shell),和面向内部的内核(Kernel)。
3、操作系统的内核
Kernel-操作系统内部组件,包括:CPU调度、物理内存管理、虚拟内存管理(给上层应用提供相对独立而且尽可能大的内存)、文件系统管理(存储和访问永久保存的数据)、中断处理与设备驱动(和底层设备直接打交道)等。
- CPU调度器
- 物理内存管理
- 虚拟内存管理
- 文件系统管理
- 中断处理与设备驱动
4、操作系统(OS Kernel)的特征
(1)并发。计算机系统中同时存在多个运行的程序,需要OS管理和调度。注意区分并发和并行。并发指的是在一段时间内有多个程序可以运行。并行指的是在一个时间点上有多个程序可以同时执行。“一段时间”和“一个时间点”的区别。要实现并行,一般要求有多个CPU。单核CPU无法实现并行。
(2)共享。多个应用程序在宏观上可以“同时”访问内存、I/O等资源,但在内部可能是互斥的(同一个时间点,某些资源只能由一个程序使用)。
(3)虚拟。利用多道程序设计技术,把一台物理机器虚拟成多台虚拟机器。让每个用户都觉得有一个计算机专门为他服务。
(4)异步。程序的执行不是一贯到底,而是走走停停的,向前推进的速度不可预知,取决于系统的管理。但只要运行环境相同,OS需要保证程序运行的结果也要相同。
5、操作系统的结构的发展演变
(1)简单操作系统:MS-DOS(1981-1994)不分块的单体内核
(2)微内核的设计,尽可能把内核的功能移到用户空间。应用的服务通过消息传递机制的松耦合方式进行交互。产业界用得比较少。
(3)VMM,虚拟机监控器。从操作系统-硬件变为操作系统-VMM-硬件。多操作系统共享硬件资源。
二、启动、中断、异常、系统调用
1、启动
操作系统一开始是放在DISK(硬盘)中,并不是放在内存中。
BIOS:基本I/O处理系统。内存中有一部分空间(固定的)是用来存放BIOS的。
以X86为例:
-
X86 PC开机时,CPU处于实模式,这时候内存的计算方式是 段基址 <<4 + 段内偏移;
-
CPU的第一条指令是通过CS:IP来取得,而此时CS=0xFFFF,IP=0x0000。这是硬件设定好的。
-
所以最开始执行的指令地址就是0xFFFF0,放在这里的是个跳转指令,调到系统BIOS中真正的启动代码处:主板的BIOS ROM(只读存储区)中。
-
系统BIOS的启动代码首先进行加电自检(POST,Power-On Self Test),检测系统中的一些关键设备是否存在和能否正常工作,例如内存和显卡等。
-
寻找显卡的BIOS。
-
将BootLoader加载到内存中。BootLoader一般放在硬盘的第一个主引导扇区,也就是磁盘0磁道0扇区(一般512个字节)。比如在X86中,BIOS将BootLoader加载在0x7cdd。
-
之后BootLoader将操作系统的代码和数据从硬盘加载到内存中。
完成上述操作后,跳转到操作系统的起始地址,将计算机系统的硬件管理交给OS。
三、操作系统与设备的程序交互为什么应用程序不能直接访问外设,而必须通过操作系统?
(1)在计算机运行中,内核是被信任的第三方,而应用程序不是,如果直接访问外设,可能带来安全问题。
(2)只有内核可以执行特权指令。
(3)为了方便应用程序,给应用程序提供简洁的接口。
四、中断、异常、系统调用处理机制
定义
1、系统调用
定义:应用程序主动向操作系统发出服务请求,来源于应用程序。异步或同步。应用程序知道它什么时候会发出系统调用请求,所以发出请求是同步的,但应用程序不知道操作系统什么时候会返回一个结果,因此是异步的。应用程序需要等待系统调用的响应,之后才能继续执行。
2、异常
定义:非法指令或者其他坏的处理状态(来源于不良的应用程序的操作引发的事件),如内存出错等。是个同步事件,指的是应用程序可以知道这件事情什么时候会发生,比如除以0,一定会产生个异常。响应状态:杀死或重新执行意想不到的应用程序指令。
3、中断
定义:来自不同的硬件设备的计时器和网络的中断(来源于外设,键盘、鼠标、网卡等,产生各种事件)。是个异步事件,应用程序不知道什么时候会发生这件事。响应状态:中断对应用程序是透明的。
说明
1、中断
每种中断有个特定的编号(比如不同的外设会有不同的编号),收到中断后,CPU去访问中断表,在表中查到对应的中断服务程序的地址,从相应的地址去执行。
中断流程
具体是通过硬件和软件两部分来执行的
硬件:
(1)设置中断标记[CPU初始化]。外设产生一个标记,发给CPU。CPU根据这个标记产生一个中断号,将中断号发给操作系统。
软件:
(1)操作系统保存当前处理状态(当前执行的指令的地址和当前一些寄存器的内容)。
(2)操作系统根据CPU给的中断号,访问中断服务程序,对中断进行处理。
(3)处理完后,清除中断标记。
(4)恢复之前保存的处理状态。
因此,上面的过程对应用程序是透明的。
2、异常
应用程序执行特定的指令会触发异常,CPU得到异常ID号。首先保存现场,然后根据异常ID号进行处理。可能会杀死应用程序,也有可能进行弥补后,恢复现场,重新执行。
3、系统调用
应用程序通过接口来对操作系统的服务进行请求。比如在标准C库中,应用程序调用printf(),会触发系统调用write()。操作系统完成请求后,会返回成功或失败,让应用程序继续执行后续工作。应用程序访问主要是通过高层次的API接口,而不是直接进行系统调用。比如Win32 API用于Windows,POSIX API用于POSIX-based systems,包括Linux、Unix、Mac OS X的所有版本。Java API用于Java虚拟机等。
系统调用的实现:应用程序直接或间接通过一个library(库)访问系统调用接口,一旦访问之后,会触发操作系统从用户态到内核态的转换,控制权从应用程序交到操作系统手中。操作系统对系统调用进行识别,并进行需要的服务。
用户态与内核态
用户态:CPU执行的特权级比较低,不能直接访问一些特殊的机器指令和I/O。
内核态:CPU可以执行任何指令,包括特殊的机器指令和I/O。此时操作系统可以完全控制整个计算机系统。
中断、异常、系统调用的区别
系统调用和函数调用的区别:
(1)应用程序发出函数调用时,在一个栈空间里进行函数间参数传递和参数的返回。
(2)发出系统调用时,应用程序和操作系统拥有各自的堆栈。当应用程序发出系统调用时,需要切换堆栈,并触发操作系统从用户态到内核态的转换。因此系统调用的开销比函数调用大很多。系统调用的好处就是安全、可靠。
五、跨越操作系统边界的开销
上述的中断、异常、系统调用,跨越了操作系统的边界。有一定的代价,这代价是为了确保系统能安全可靠的运行。在执行时间上的开销超过程序调用(前面提到了)。
开销:
(1)建立中断、异常、系统调用号与对应服务例程映射关系的初始化开销。这个表要在操作系统正常工作之前就建好。
(2)维护操作系统自己的内核堆栈。这个内核堆栈在操作系统开始工作之前就要建立好,操作系统退出的时候保存,执行的时候恢复。应用程序堆栈的保存和恢复,因此也要进行维护。
(3)验证参数。操作系统不信任应用程序,需要对系统调用参数进行检查(安全上的时间开销)。
(4)内核态映射到用户态的地址空间。
(5)内核态独立地址空间TLB。
这些开销都是值得的,必须的,为了保证操作系统在安全可靠的环境中执行。