引言 #
本文是基于mit6.828 的lab1对操作系统的思考,网上有不少关于lab1的博客,大部分都是介绍如何完成lab1的问题,介绍的比较详细的有这个博客,在这里我就不从问题出发,建议大家看完上面的博客在看我这篇博文,我这篇博文就是从把我遇到的疑惑提炼出知识点,然后再把这些知识点串起来
实验目的 #
作为操作系统的第一个实验,在完成lab1我们主要目的就是要了解什么是操作系统,后面的lab都是在这个基础上搭建完善好系统的各种功能,就好像你画一幅画,第一步你先把人物的轮廓外形画出来,接下来你在把人的鼻子耳朵眼睛等等慢慢画出来,所以第一个实验非常重要,如果不把骨架搭好,你的操作系统再怎么豪华强大也是空中楼阁,所以我们在这个lab上一定要花上很多时间,万事开头难,只要攻克了这个,操作系统也就离你不远了
前言 #
谈操作系统之前必须得谈谈什么是计算机,因为我们的操作系统是运行在计算机上面的,操作系统与计算机关系密切,我们可以用一个很贴切的比方,假如你想了解手套为什么长成那么奇怪,那么你得先举起你的双手看看。
闲话不多说,进入正题,计算机最核心部分就是CPU,记得电脑刚发明出来的时候,编程只能通过穿孔卡片,当然计算机发明这么久,CPU的核心功能基本上没有变,还是只能通过“穿孔卡片”,只不过我们编程语音将我们的使用的语音如C,Python等变成“穿孔卡片”,这里有个非常重要的概念:指令。也就是每个CPU的是每次只能执行一条指令。当然第二个重要的就是内存了,前面说了CPU是必须要通过”穿孔卡片“来编程的,但是随着程序愈来愈大,我们必须要使用一个东西来存贮“卡片”,所以内存孕育而生了,其实很多感叹电脑这个词实在是太贴切了,人脑可以处理问题,电脑也可以处理问题,人脑有短期记忆长期记录,电脑有寄存器(短期记忆)内存(长期记忆)。
简单的谈了谈计算机,我们提出两个重要的前提
- 计算机通过顺序读取一条一条指令来工作
- 计算机从内存获取指令
所以操作系统的就是在内存上面运行自己并帮助其他程序的一个大程序,总的来说它就是内存上面的一段程序,当然它的功能就是好好在它一亩三分地上面“分封诸侯”,维护自己的“王位”。
由于一开始它只是待在软盘或者磁盘等存贮设备上的一段程序,接下来我们就看操作系统如何“上位”内存的“宫斗大戏”。
上位篇 #
在操作系统“上位”之前,其实计算机还进行了一个小操作系统(BIOS)运行,具体的流程可以看这篇文章,如果把电脑看做人的话,BIOS就是我们的脊髓,它控制我们的身体四肢(硬件),但是却无法灵活的操作他们(比如非条件反射的膝跳反射),但是它却不可少,少了它就成植物人了。BIOS的作用总结起来就是感知硬件,调控硬件(比如CPU电压,内存频率)。
BIOS与操作系统最密切的一个地方就是设置启动盘,大家可能大多时候都是听文件文件比较多,但是文件这个概念其实是操作系统的,在没有操作系统之前是没有文件这个概念的,只有盘这个概念,比如一块硬盘、一张软盘、一个U盘,所以BIOS的一个重要工作就是选定一个存储硬盘,然后读取上面的程序运行。
所以我们从这时候可以得到一个结论,安装操作系统,只需要按照BIOS的规定把程序放到某一个存贮设备(磁盘、U盘、软盘)专门位置上(一般是头部开始),开机后然后就静等BIOS把程序加载到内存,就完成了“上位”过程。
我们已经知道操作系统的外部条件,接下来分析一个操作系统到底该有啥
操作系统结构 #
首先我们要回顾一下操作系统的发家史,刚开始是没有操作系统的这个概念的,随着在电脑上要运行的越来越多,每个程序都要写大量底层代码来操作硬件这样无疑每个程序越来越来臃肿,所以我们需要让操作系统管理来管理硬件,这样不但能够提供程序兼容性(一次编译、到处运行),而且能高效利用硬件(操作系统专供)。所以操作系统也就是程序的分工的产物
接下来我们看看操作系统的发展历史,大家都知道汇编语言是除了机器语言最接近底层的一种语言,第一个Unix系统一开始也就是使用汇编编写的。但是汇编语言不易于维护,所以1973年汤普逊和里奇用C重构了Unix,但是部分与硬件接触太大的地方还是必须使用汇编(in、out端口,操纵指定寄存器等),所以现代操作系统还是用C与汇编混合编写的(C占大部分),当然最后都会编译成机器码,所以我们从机器码角度来看,最后的结果都是相同的,不同的是我们使用不同的工具来生成机器码
前面我们介绍了启动盘,其实BIOS给我们只是从启动盘上面取了一小段数据(第一个扇形区)放到内存上面(512B),然后执行那小段上面的机器码,所以接下来就有一个问题,随着我们的操作系统功能越来越完善,取出来的代码肯定不是全部代码,代码只有放到内存上面才能跑,所以前人就把代码分成两部分,一部分称作引导,另一部分才是核心代码,引导作用就是把核心代码放到内存上面去
所以操作系统被分成两部分
- 引导
- 核心
这里比较有意思的地方就是引导和核心如何交换程序控制权,由于要讲解必须要牵扯代码,所以我把这部分分离出来,放到这篇博客上面,要了解细节可以看这篇博客。
现在我们假设你已经知道引导将核心代码加载在了内存上面,现在我们就从这里开始分析,堆栈作为程序的基础,首先我们要提一下堆栈
堆栈 #
什么是堆栈呢?看下面这张图
堆顶为大地址,我们使用的时候只要不断把栈顶往下推,就能将线性内存变成一个数据结构。当然有个问题,我们必须要知道什么地方是栈顶,现在我们想想操作系统必须要放到内存的某一个地方,假如放到低位置,那么我必须要记住操作系统的最高位置,如果不这样做当堆栈顶到操作系统的存贮地址,那么操作系统就被破坏了。所以我们只能把操作系统放到高的内存地址,这样我们既能安全的使用堆栈,而且保证了操作系统的安全
所以我们接下来就谈谈由于这个设定引发的一系列问题
操作系统的高地址 #
在lab1中,操作系统的起始代码被设定为0xF0100000
的高位
从这个地址我们可以分析出来什么0xF0100000
,因为我们知道32位操作系统最高只能有4G内存,也就是2的32次方内存,这个地址代表的是系统最后面的255M内存的空间,也就是操作系统给自己留了255M剩余内存,给前面保留了(4G-255M)空间
操作系统这个设计是非常好的,不仅给自己留下扩展空间,也给其他代码带来便利,就是从0-(4G-255M)这部分内存随便用,怎么改都不会出问题
从现在角度来看,个人电脑随便都是2G、4G内存,但是在70\80年代,几M内存都很很大的存在,如果我内存没有4G这么大,那么这个操作系统就无法使用了,而且你留下256M给操作系统,有的时候我整个电脑内存都没有这么大,留下那么多,就是浪费
所以最后他们想出来一个办法,就是现实和理想之间放一个转换器(MMU),也就是动态内存管理。操作系统也不管你机主有多大内存了,我假设你有4G,当你用一块,无论是什么位置,我从空余的地方给你取出来,假如你没有4G,我就把一部分不常用内存值的放到磁盘上面,这样通过这样这样的操作的实现内存的高效利用。
操作系统对内存这种骚操作就相当于在更高的维度上建立了一种抽象,不论底层硬件怎么变(内存大小不同),我高层都不需要变,只需要底层把根据实际情况依次映射,这样无论你换多大内存,我都不需要重装系统。
这种映射不通过代码很难将清楚,所以我这里也不提太多,如果你想知道怎么映射,硬件如何配合,你可以看我这篇博客,详细介绍了内存分页的骚操作。
总结 #
至处到这里我们的操作系统之旅就结束了,我们成功在引导帮助下上了内存,也使用了提高操作系统的地址保证了堆栈的正常使用,接下来就是操作系统的各种附加功能,我们也会在接下来的lab中继续讲解。
lab1不止介绍了这两个地方,但是在lab1中我觉得最重要的地方就是要知道操作系统是什么,它从哪里来,它要到哪里去,操作系统毕竟牵涉到硬件,很多硬件知识我们不一定能搞懂,比如说要切换哪几个位到保护模式,怎么输出端口才能读取扇形区,但是我们必须要搞懂他这样做的原因,这也是我在这篇里面谈了很多操作系统历史的原因,希望在理解操作系统上面能给大家带来一点启发。
在最后的话,我谈一谈我对入门这门课的感受吧,我算编程基础还算比较扎实,但是在一开始学习的过程中一脸懵逼,C语言的各种骚操作,汇编与C疯狂混合,看惯了脚本语言的我,对这种改一下就编译不过的“硬骨头”异常难受,一开始根本看不下去,有的时候看几行代码,查资料好几个小时,而且资料特别难找,你不知道这种骚操作有啥意思,在这里还得感谢学习过这门课的学长学姐们,在网上无私的把自己的感受分享出来,慢慢的根据他们的资料和自己的调试还有自己找资料,慢慢的把这个代码一行一行搞懂,搞懂之后特别感慨,其实也不是很难,难的是要把你知道的所以知识点串起来,然后你就能搞懂为什么要这么做,所以我推荐大家还是多看代码,多去思考为什么要这么做,必须要自己的思考在里面,如果单纯的知识为了完成实验其实很简单,但是难就难在把这门课琢磨透,搞懂他们为什么要这样考你,毕竟大家最后的目的都是自己做出自己的操作系统,只有了解的透彻,才能“破而后立”搞出自己的创新,而不是复制他们的壳子,所以加油把每一行代码都搞懂,多思考,不要骄傲,毕竟这只是教学类的操作系统,要想自己写出来还得不懈努力。
接下来还有6个lab需要完成,终于迈出的第一步,希望在年前能够完成所有的实验吧!!!加油!!!