清华操作系统课程学习笔记1

最近深感自己操作系统的基础不够扎实(学过的内容全还给老师了),所以就回笼重新学了一遍,这个系列也就是我在学习过程中做的一些笔记。我主要是根据清华大学的操作系统视频课程来学习的。在这里不得不感慨一下,B站真是个神奇的地方,居然有人把这门课的视频都整理好了:

https://www.bilibili.com/video/av6538245(以概念、原理讲解为主)

https://www.bilibili.com/video/av10496140(有比较详细的实验讲解)

上面两个视频,其实就是一门课,只是不同的老师在讲。

实验环境:https://www.shiyanlou.com/courses/221

因为我是为了补充自己在基础知识上的不足,所以并没有做实验。

另外,在写笔记的过程中,也搬运了一些其他博客的内容,有些地方没有标注来源,如果原创作者觉得有任何问题都可以联系我。


第一章 课程概述

1、什么是操作系统

从用户、控制的角度,操作系统是个控制软件,管理应用程序、为应用程序提供服务、杀死应用程序。从资源分配的角度来说,操作系统进行资源管理、管理各种外设、对资源进行分配。操作系统将物力资源进行抽象:CPU抽象成进程、磁盘抽象成文件、内存抽象成地址空间提供给应用程序使用。

2、操作系统层次结构

硬件之上、应用程序之下。是个中间层的系统软件。

操作系统位于应用软件之下、为应用软件提供服务支撑,完成对硬件的管理。

操作系统有两层对外的接口:对外暴露的接口外壳(Shell),和面向内部的内核(Kernel)。

3、操作系统的内核

Kernel-操作系统内部组件,包括:CPU调度、物理内存管理、虚拟内存管理(给上层应用提供相对独立而且尽可能大的内存)、文件系统管理(存储和访问永久保存的数据)、中断处理与设备驱动(和底层设备直接打交道)等。

4、操作系统(OS Kernel)的特征

(1)并发。计算机系统中同时存在多个运行的程序,需要OS管理和调度。注意区分并发和并行。并发指的是在一段时间内有多个程序可以运行。并行指的是在一个时间点上有多个程序可以同时执行。“一段时间”和“一个时间点”的区别。要实现并行,一般要求有多个CPU。单核CPU无法实现并行。

(2)共享。多个应用程序在宏观上可以“同时”访问内存、I/O等资源,但在内部可能是互斥的(同一个时间点,某些资源只能由一个程序使用)。

(3)虚拟。利用多道程序设计技术,把一台物理机器虚拟成多台虚拟机器。让每个用户都觉得有一个计算机专门为他服务。

(4)异步。程序的执行不是一贯到底,而是走走停停的,向前推进的速度不可预知,取决于系统的管理。但只要运行环境相同,OS需要保证程序运行的结果也要相同。

5、操作系统的结构的发展演变

(1)简单操作系统:MS-DOS(1981-1994)不分块的单体内核

(2)微内核的设计,尽可能把内核的功能移到用户空间。应用的服务通过消息传递机制的松耦合方式进行交互。产业界用得比较少。

(3)VMM,虚拟机监控器。从操作系统-硬件变为操作系统-VMM-硬件。多操作系统共享硬件资源。

6、总结

课程概述、什么是操作系统、为什么学习操作系统、操作系统实例、操作系统的演变、操作系统的结构。

第二章 启动、中断、异常、系统调用

一、启动

操作系统一开始是放在DISK(硬盘)中,并不是放在内存中。

BIOS:基本I/O处理系统。内存中有一部分空间(固定的)是用来存放BIOS的。

以X86为例:

1.      X86 PC开机时,CPU处于实模式,这时候内存的计算方式是 段基址 <<4 + 段内偏移;

2.      CPU的第一条指令是通过CS:IP来取得,而此时CS=0xFFFF,IP=0x0000。这是硬件设定好的。

3.      所以最开始执行的指令地址就是0xFFFF0,放在这里的是个跳转指令,调到系统BIOS中真正的启动代码处:主板的BIOS ROM(只读存储区)中。

4.      系统BIOS的启动代码首先进行加电自检(POST,Power-On Self Test),检测系统中的一些关键设备是否存在和能否正常工作,例如内存和显卡等。

5.      寻找显卡的BIOS。

6.      将BootLoader加载到内存中。BootLoader一般放在硬盘的第一个主引导扇区,也就是磁盘0磁道0扇区(一般512个字节)。比如在X86中,BIOS将BootLoader加载在0x7cdd。

7.      之后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。

这些开销都是值得的,必须的,为了保证操作系统在安全可靠的环境中执行。


猜你喜欢

转载自blog.csdn.net/qq_26286193/article/details/80222529