我一个做前端的,要了解操作系统?

2,853 阅读14分钟

操作系统是一个大而复杂的话题,如果想要用一篇文章讲清楚操作系统,那简直就是不自量力了,所以这篇文章是从大框架上分享一下我理解的操作系统,话不多说,发车!

一、操作系统概念

小故事:2019年8月9日华为发布了自己的鸿蒙操作系统,打破了长期以来国内对国外操作系统的依赖,虽然现在鸿蒙的市场占有率还远远落后于国外的操作系统,但是对于国内操作系统的发展是非常有意义的。

问题1:什么是操作系统?

一台现代计算机设备,比如手机、电脑、是由软件和硬件共同组成,缺一不可。如果我们的手机或者电脑没有软件的话,对我们而言就是一块板砖,毫无用处。如果需要使用硬件的计算能力需要通过编写程序跑在硬件上,而编写的这个程序就是软件。而软件也分为应用级软件系统级软件,我们电脑上会安装比如360、微信、QQ、浏览器等软件都属于应用级软件,这些软件不直接和硬件沟通,而是调用操作系统的接口来实现对应的功能,而各种操作系统其实就是一款直接和硬件打交道的软件,操作系统需要管理内存、文件、进程、IO等设备。当然再细分的话,操作系统可以分为桌面操作系统,手机操作系统,服务器操作系统,嵌入式操作系统等,而今天主要以桌面操作系统来进行介绍,下面用一张图来表示他们的关系:

截屏2023-01-02 下午1.47.00.png

问题2:为什么要分为系统级软件和应用级软件呢?

在经济学中有一个分工原理的概念,大概的意思就是说在商业世界里,因为分工越来越明确,专门的人干专门的事情,效率也会越来越高。计算机的世界里也是追求效率的,甚至比现实世界更加追求效率,这就要求分工要尽可能的明确。所以大家试想一下,如果没有操作系统这个更底层的软件,那么第一:像微信、QQ这些软件就需要自己去管理内存、文件、输入、输出设备。这就极大的增加了程序编码人员的心智负担和学习成本,不利于软件行业的发展。第二:应用程序之间可能会存在打架的现象,因为各个应用程序之间都是自己去抢占CPU的资源,没人负责调度和管理,那就相当于一群猴子,没有猴王管理,那就乱套了。

因此这样的分工是非常有必要的,操作系统负责管理和调度应用程序,操作系统通过某种机制去决定谁去占用CPU资源,不让应用程序因为CUP资源而打架,其次操作系统统一来和硬件打交道,负责管理内存、进程、硬盘、IO等设备,对外暴露接口给上层的应用程序。而应用程序就可以专注于业务的开发,不需要去操心怎么更好的管理内存、硬盘等设备。

二、硬件

现代计算机都是符合冯诺伊曼体系的,即一台计算机存储单元、计算单元、控制单元、输入设备、输出设备组成。如果对标现代计算机中的各种设备,那么就有以下的关系。

截屏2023-01-02 下午3.17.19.png

问题3:32位和64位是什么意思?

大家如果查看自己电脑的配置,可能会看到这样的配置信息

31672644909_.pic.jpg

64位操作系统,基于x64的处理器。这个是什么意思呢?简单来讲,这个多少位代表位宽,即计算机在线路上一次性可以传输多少位的数据,也代表处理器一次性可以计算的数的大小。

因此基于32位的处理器代表处理器一次性可以计算0 - 4294967296之间的数,而不需要额外的转换。同理基于64位的处理器代表一次性可以计算0-2的64次方的之间的数而不需要额外的转换。当然现实生活中我们其实很少计算超过42亿这个数的需求,所以64位处理器的优势要在大数计算的情况下才能有所体现。

如果用 32 位 CPU 去加和两个 64 位大小的数字,就需要把这 2 个 64 位的数字分成 2 个低位 32 位数字和 2 个高位 32 位数字来计算,先加个两个低位的 32 位数字,算出进位,然后加和两个高位的 32 位数字,最后再加上进位,就能算出结果了,可以发现 32 位 CPU 并不能一次性计算出加和两个 64 位数字的结果。

以上我们说明的是硬件的 32位 和 64位的区别,相信大家都现在都能理解了,但其实软件也有32位和64位的区别。我们说操作系统也是一个软件,因此有以上的多少位操作系统的说明,但有的时候我们下载应用软件的时候,也会有这样的选项。

截屏2023-01-02 下午3.34.54.png

软件的多少位代表的其实是指令的位数,软件在交给硬件运行的时候,是需要编译为二进制的机器码的,也就是一堆010100101,而64位的软件的程序当中访问的是64位大小的地址空间,这个地址空间交给32位的处理器是运行不了的。32位的处理器没有那么大的寻址能力。就相当于火车和过山车都有自己的轨道,但火车没办法跑在过山车的轨道,因为火车太宽太大了,过山车的轨道根本无法承受。

三、操作系统结构

既然要了解操作系统,那么首先需要学习一下操作系统的结构。

操作系统最为核心的部分就是内核,在服务器操作系统 —— linux操作系统中,最为核心的其实就是linux内核。而由于linux是开源的,所以基于linux内核,经过各种各样的组织和公司的迭代开发,发行了很多分支的操作系统,比如centOS、ubuntu、Fedora等。对于windows操作系统来说也是一样的,也有windows内核,只不过windows操作系统并不开源,如果想要开发windows操作系统,只有进入微软公司才有机会!

下面用一张图来介绍内核【kernel】和硬件的关系。

对于内核的架构一般有这三种类型:

  • 宏内核,包含多个模块,整个内核像一个完整的程序;
  • 微内核,有一个最小版本的内核,一些模块和服务则由用户态管理;
  • 混合内核,是宏内核和微内核的结合体,内核中抽象出了微内核的概念,也就是内核中会有一个小型的内核,其他模块就在这个基础上搭建,整个内核是个完整的程序;

Linux 的内核设计是采用了宏内核,Window 的内核设计则是采用了混合内核。

四、内存管理

问题4:什么是内存?

内存是由DRAM(动态随机存储器)芯片组成的。DRAM的内部结构可以说是PC芯片中最简单的,是由许多重复的单元——cell组成,每一个cell由一个电容和一个晶体管(一般是N沟道MOSFET)构成,电容可储存1bit数据量,充放电后电荷的多少(电势高低)分别对应二进制数据0和1。

我们可以把内存想象成为一个二维的数组,每一个格子都具备存储一定大小的数据的能力,只不过这个能力需要一直通电才可以维持,断电之后这些数据就丢失了。

截屏2023-01-02 下午4.15.32.png

操作系统管理内存的方式叫做虚拟内存技术,上面的图是代表的是硬件,这种客观现实的内存叫做物理内存。而我们写应用程序时声明的变量,访问的内存其实是虚拟内存,例如:

const a = '我是一个变量'

console.log(a)

当我们要打印这个变量时,js引擎需要访问这个变量所对应的内存地址,而这个内存地址其实是虚拟内存地址,而非物理内存地址。虚拟内存地址和物理内存地址会有一个映射关系,这种映射关系便是由操作系统内核和硬件的配合共同来维护和实现,确保我们访问的内存地址能够准确访问到相应的数据。

问题5:为什么要有这个虚拟内存地址呢?直接访问物理内存不行么?

如果我们的计算机永远只跑一个程序,那其实不用虚拟内存技术其实是可以的,但是大家想一下我们的计算机是不是同时在跑很多个程序。一个程序如果直接访问物理内存,那么如果a程序明明维护好了一个变量,b程序在某个时候不小心把a程序维护的这个变量给改了,那a程序不就der了么?这就好比你有一个女朋友,你们明明关系很好,现实世界里很容易突然半路杀出一个高富帅杀出来给你截胡了,你是不是很气。所以不能够直接访问物理内存。那怎么办呢?你想了一个办法,你创建了一个平行宇宙,在这个平行宇宙里,只有你和你女朋友两个人,别的男人根本就没办法到达你这个平行宇宙。所以你很开心,终于可以不怕女朋友被别人抢走了。

虚拟内存就是这样一个技术,对于a程序而言,他就像拥有一块独立的内存空间一样,别的程序永远无法访问a的空间。虚拟内存提供了一个安全的沙箱隔离环境。

当然作为上帝视角,我们知道这其实都是给a程序的假象,实际上只不过是操作系统在物理内存中隔离出若干个空间,让每个程序只能访问或写入属于自己的那一块空间而已。

五、进程管理

对于操作系统而言,在他的上层运行着若干个程序,这些程序都是一个又一个进程。我们可以把这多个进程看作一个又一个任务,表面上似乎CPU同时处理着这些任务,但其实微观层面并不是,对于一个CPU而言,同一时刻只能处理一个任务,然后CPU不停的切换处理的任务,由于切换的频率非常快,所以宏观上我们就感觉好像是同时在处理很多任务,所以我们建立一个模型。

如果有先后产生了 A、B、C、D四个任务,作为操作系统,是不是首先要考虑在当下时刻该把谁交给CPU去执行?

截屏2023-01-02 下午5.03.24.png

这就要提到进程的调度算法了?

先来先服务调度算法

既然任务的产生有先后,我们可以通过一个队列去维护任务,先产生的进入队列,然后每次从队列中取出一个任务,交给CPU执行后,再取出下一个,直到队列清空。

但是这样做有一个问题,那就是如果先加入队列的任务执行时间比较长的话,那么后面来的短任务就需要等很长的时间,这对于短的任务显然是不公平的,会让短任务等很长的时间。

最短作业优先调度算法

那我们改进一下,依然维护一个队列,每产生一个任务,我们判断一下所需要的时间,然后根据时间排一下顺序,短任务放前面,长任务放后面。

这样做虽然弥补了上面的问题,但是又产生了新的问题,如果新来的都是短任务,那么长任务就没有机会执行,这样的话显然对长任务又不公平了。

时间片轮转调度算法

比较公平的算法应该是这样的,维护一个队列,我们给每一个任务分配固定的时间片,对于短任务,可能是刚好用完时间片,或者没有用完提前结束,就从队列中取出下一个任务。对于长任务,分配的时间片一旦结束,就直接中断执行,保存好上下文信息,将其加入到队尾,继续从队列中取出下一个任务,直到队列清空。

这个方法显然是公平的,但是绝对的公平真的好么?不一定,因为对于用户而言,应该需要有优先级的概念,比如用户移动了鼠标,敲击了键盘,CPU应该首先响应,处理这个任务,否则给用户的感觉就是卡顿和迟钝的。因此操作系统需要把这一层考虑到位。

最高优先级调度算法

在时间片轮转的基础上,我们可以把每个任务产生的那一刻分配一个优先级,如果有高优先级,我们首先加到队首,对于低的优先级,退而求其次放到队尾,这样的话,算是解决了这个优先级的问题。

但是有个问题,优先级似乎在产生的那一刻就确定了,不能再改变了,这对于系统的运行显然是不利的。

多级反馈队列调度算法

「多级」表示有多个队列,每个队列优先级从高到低,同时优先级越高时间片越短
「反馈」表示如果有新的进程加入优先级高的队列时,立刻停止当前正在运行的进程,转而去运行优先级高的队列;

来看看,它是如何工作的:

  • 设置了多个队列,赋予每个队列不同的优先级,每个队列优先级从高到低,同时优先级越高时间片越短
  • 新的进程会被放入到第一级队列的末尾,按先来先服务的原则排队等待被调度,如果在第一级队列规定的时间片没运行完成,则将其转入到第二级队列的末尾,以此类推,直至完成;
  • 当较高优先级的队列为空,才调度较低优先级的队列中的进程运行。如果进程运行时,有新进程进入较高优先级的队列,则停止当前运行的进程并将其移入到原队列末尾,接着让较高优先级的进程运行;

可以发现,对于短作业可能可以在第一级队列很快被处理完。对于长作业,如果在第一级队列处理不完,可以移入下次队列等待被执行,虽然等待的时间变长了,但是运行时间也会更长了,所以该算法很好的兼顾了长短作业,同时有较好的响应时间。

六、总结

我们来总结一下,本篇博客从框架方面介绍了一下操作系统,涉及到操作系统概念、硬件组成、内存管理、进程管理等。笔者能力有限,很多细节其实都没有办法谈起,例如操作系统置换算法、文件管理、虚拟内存和物理内存如何映射的等等。参考资料中有笔者认为不错的内容推荐,大家可以自行学习。最后感谢大家的阅读,有不正确的地方还请大神评论区指出。

参考资料

1.鸿蒙操作系统

2.linux就该这么学

3.冯诺伊曼体系结构

4.小林的操作系统课