冯诺依曼体系结构 基础 概念认识
我们现在常见的 计算机,如 笔记本电脑,或者是不常见的 计算机,如服务器,大部分都遵循一个体系结构 -- 冯诺依曼体系结构。计算机的基本构成 就是由 冯诺依曼体系结构 来构成计算机的基本单元的。
而,冯诺依曼体系结构非常的简单,就是由输入设备输入数据,保存到 存储器当中,然后将数据读取到 运算器当中,进行逻辑运算,再把运算结果 写会到 存储器当中,输出设备就可以从 存储器当中读取数据进行输出操作。这就是 冯诺依曼最基本的 操作流程,如下图所示:
上述就是冯诺依曼体系结构。他是一个计算机硬件设计的架构,冯诺依曼表示,计算机要能够正常的工作,就要遵循他所提出的体系结构来设计。
那么,对于这个体系当中的 输入输出设备,还有中央处理器你或多或少都是有了解,而且有接触过的。
但是其中存储器是什么呢?是外存?内存?寄存器?
答案是内存。
一般,像磁盘,u盘这些一样有存储能力的设备, 叫做外存,一般是指的是 输入输出设备。内存 和 外存之间,在体系结构上差别特别大。
输入/输出设备
计算机是给人提供计算服务的,但是,计算机在帮助人计算数据的前提是,人要把数据输入到计算机当中,所以,输入这一步骤是不可或缺的。
而且,计算机是只认识 二进制数据的,还是以各种电子线路,信号来控制的。
但是人输入的数据不会是输入二进制数据的,这样不仅不好观察数据,同时数据二进制的数据非常的麻烦。
所以,就需要有一个设备,把人输入的数据,转换成二进制数据,然后再交给计算机。这就导致了,然不能直接去访问到计算机当中内存,运算器等等元件,只能通过这么一个设备去和计算机进行数据交流。
比如:我们通过键盘给计算机输出了 "ABC" 这个字符串,但是计算机是不认识这个字符串的,他只认识 二进制数据,计算机通过这个字符串,计算出了 "abc" 小写的字符串,输出 给我们看。但是计算机输出的是二进制数据,我们是看不懂。
所以,基于以上问题,就需要两种设备,帮助我们更好的和计算机进行交流 -- 输入输出设备。
输入输出设备:是 帮助 人和计算机,计算机和计算机之间进行交互的外部设备。(输入输出设备 是 外部设备,简称外设)。
常见的输入输出设备:
- 输入设备:键盘,摄像头,麦克风,鼠标,磁盘,网卡。
- 输出设备:显示器,扬声器,播放器硬件,磁盘,网卡。
发现,有的设备是纯输入,或者纯输出,也有既是输出也是输入的设备。
运算器和控制器(中央处理器)
运算器:是用来对数据进行计算任务。
而计算任务分为 算数运算 和 逻辑运算。算数运算就是类似于 1 + 1 = 2 这种运算;而 逻辑运算 比如 条件判断,逻辑与 和 逻辑或等等的判断。
有了运算器,就可以 对输入的数据进行运算,然后把运算结果进行输出。
但是,如何去控制 什么时候从输入设备中 提取数据到 存储器; 输出设备在什么时候对存储器当中的数据进行输出。什么时候 运算器从 存储器当中读取数据进行计算,运算器什么时候把计算结果写回到 存储器当中。
这时候就需要控制器,对我们 计算硬件流程进行一定的控制。
我们把 运算器和控制器 称为 中央处理器(cpu)。
cpu 当中其实还有很多寄存器空间,这里先不做介绍。
系统总线 和 IO总线
在冯诺依曼体系结构当中的五大功能硬件都是独立的个体, 在计算器当中这五大元件我们都是可以独立拆卸出来的。要进行这几个设备之间的互相数据流通的话,就需要把这几个元件 用 “线” 连接起来。
这些 “线” ,我们称为总线,总线由分为 系统总线 和 IO总线 。
cpu 之间 , cpu 和 存储器之间 连接的总线称为 系统总线。
存储器 和 输入输出设备之间 连接的总线称为 IO总线。
存储 大小 和效率 关系
关于顺序表的和链表的cpu高速存储-CSDN博客
从上图 和 上述文章当中我们知道,存储 是分级别的,而且各有优势。
而 之所以 cpu不直接从 输入设备 读取数据,直接进行计算,就是因为 cpu 很快,外设的 效率很慢,更不上 cpu 的读写速度。
有一个 理论是 木桶原理:
他给我们的启示是,一个木桶能装多少水,不是看 最长,或者 中间长度的模板有多长,而是看最短 木板有多长,那么最多就能装多少水。在 cpu 当中也是一样的,你cpu 再快,存储 数据的 硬件效率很低,那么 cpu 的效率也是和 存储 数据硬件的效率是一样的。那么cpu 的效率就大大浪费了。
虽然,冯诺依曼当中使用内存效率也比 cpu 要满,但是比 外存要好太多了,所以在 冯诺依曼体系结构当中,计算机性能的参考,是参考内存的效率的,而不是cpu。
如果我们不要 存储器,直接有输入设备 到 cpu 再到 输出设备的话,因为 cpu 当中的寄存器的存储空间是非常有限的,也就注定了 这个结构 必须是串行的。就会出现数据排队的问题。
所以,不要觉得 多一个存储器环节是不好的。
当有了存储器,可能 存储器就会把数据设备当中的数据预加载到 存储器当中,当 cpu 取存储器的当中读取数据的时候,就可以直接 存储器当中读取数据了。
此时,cpu 的加载数据和运算数据就可以同时进行了,也就是并行操作,大大提升了效率。
而这种并行操作,就是由操作系统来完成的。
所以,我们可以把 存储器 看做是一个 硬件级别的缓存空间。他是冯诺依曼体系结构当中的 核心地位。
而且,现在 cpu 只用管 把计算好的数据直接给 存储器,至于存储器如何去刷新 输出设备数据,不由 cpu 管,cpu给数据到 存储器之后,cpu 由可以继续计算下一个 运算。
一个程序为什么要先加载到 内存当中,然后在运行?
程序当中的代码最终是要给 cpu 进行运行,而要人cpu 去运行,程序的代码和数据必须要得在内存当中,cpu才能拿得到。 (这也是冯诺依曼体系结构所规定的!)
为什么,在Linux 当中实现的进度条,默认显示的数据,是会缓存起来的?在哪里缓存的?
关于进度条,看这篇博客:Linux 编写一个 简单进度条-CSDN博客
其实,就是存储在 内存当中,cpu 计算完 数据不是要把计算结果写回到 存储器当中吗?然后存储器 在以 cpu 返回的数据 刷新 输出设备,这是我们才能看到输出结果。
所以,在使用 '\r' 返回输出到屏幕上的那一个时间当中,就是存储在 内存当中的,而这个一小片区域我们称之为 缓存区,这片缓存区由代码,或者说是 代码程序进行管理。
所以,缓存才是正常的,不缓存才是不正常的。
控制器控制:
虽然在 冯诺依曼 当中cpu 和 输入输出设备之间是没有数据交互的,但是也不是完全没有交互,cpu 和 输入输出设备之间存在着 信号之间的交互,也就是cpu 当中控制器所做的事情。
输入输出设备的控制信号,是能够直接到达 cpu 当中,cpu 直接进行接收的。
关于冯诺依曼需要注意的几点
- 这里的存储器指的是内存
- 不考虑缓存情况,这里的CPU能且只能对内存进行读写,不能访问外设(输入或输出设备)
- 外设(输入或输出设备)要输入或者输出数据,也只能写入内存或者从内存中读取。
- 一句话,所有设备都只能直接和内存打交道
场景分析冯诺依曼运行过程
对冯诺依曼的理解,不能停留在概念上,要深入到对软件数据流理解上,请解释,从你登录上qq开始和某位朋友聊天开始,数据的流动过程。
从你打开窗口,开始给他发消息,到他的到消息之后的数据流动过程:
如果在上述情况下,把发送的数据 修改成 文件 的话也是一样的。只是输入输出设备不同,就像 我 从磁盘当中输入数据到 存储器当中,结果计算,给到 网课,朋友从网卡接收到数据,存储到 存储器当中,进行计算,计算结果给到 朋友电脑的磁盘当中。
为什么要有操作系统
在上述冯诺依曼介绍的五大部件,都是独立存在的,而且可以说是很笨的,因为他们只会处理自己的事情,不会考虑他们是一个团体,如何更好的进行团队协作,更好的进行交互,从而有更大的效率。
所以,此时就有了操作系统。
操作系统是一款进行管理的软件。
首先,应该管理的是 计算机当中的各个硬件;同样,操作系统还要对计算机当中的 软件进行管理,而且操作系统本身也是一个软件。
任何计算机系统都包含一个基本的程序集合,称为操作系统(OS),笼统的理解,操作系统包括:
- 内核(进程管理,内存管理,文件管理,驱动管理)
- 其他程序(例如函数库,shell程序等等)
为什么要有操作系统?
- OS 帮助用户管理好 下面 软硬件资源。
- 管理好软件硬件资源,内存使用,文件资源,驱动管理这些,都弄好,用户用起来不会出问题,就是达到了 计算机 为人类服务的作用。如果管理得不好,打十分钟游戏就蓝屏一次,那用户是不会用这个操作系统 或者说 是不会用这个电脑的。这就是操作系统的一大作用。给用户提供一个 良好(稳定,高效,安全)的运行环境。
- 给用户好的体验,这里的用户可以分为两类,一类是普通用户,一类是程序员;而普通用户使用的都是程序员开发出来的软件,所以,要想用户有良好的体验,最基本的是要程序员有良好的体验。
- 普通用户,有些不清楚底层硬件协议等等硬件信息的程序员,其实都是无法访问到 硬件的,都是通过操作系统来间接的使用到 硬件。比如我们在 VS 下写了 pirntf()函数,看似是我们写了一个软件,调用了 底层输出,输出了对应的数据。其实都是别人写好的,printf()是怎么在屏幕上输出数据的,这需要去看C源码,而C源码不是我们实现的,我们只是使用了 printf()这个函数。
- 操作系统不相信任何用户,我们之所以能使用到操作系统,是操作系统基于自己的功能,给了我们可以使用的接口(一个一个的函数),我们可以通过这个接口来实现用户想要实现的功能,或者获取到操作系统其中的数据。操作系统之所以不相信用户,是因为,在操作系统当中有很多的重要数据,不能被用户所修改,比如当前网卡的状态,是离线的,还是连接上网络的,如果此时用户能够擅自修改到网卡的状态信息,那么是会出事情的。
- 因为 操作系统使用C 语言实现的,所以,这些接口也就是 操作系统提供的 C 的一个一个函数,自己内部实现的函数调用,我们通过调用某一个函数,就可以获取到操作系统当中的某一些信息,或者实现某一个功能。我们把这种 操作系统提供的,为用户访问的接口 称之为 -- 系统调用。所有访问操作系统的行为,都只能通过系统调用完成。这些接口,当中就有外壳。
- 但是,操作系统原生的接口使用成本很高,学习成本很高,使用起来很麻烦,所以就有人对操作系统原生接口进行了上层封装,甚至有人对用这些接口实现了某些语言,供其他程序员来进行编程开发。把各种语言,接口封装在一起,就成为了库。比如C语言当中各种各样的库。
- 任何的语言,只要是使用到 硬件,都要结果操作系统,结果系统调用。
- 还有 系统的指令,也是通过操作系统的原生接口经过上层包装来实现的,指令的底层也是调用各个系统调用。我们把这种 基于系统接口上面的开发,称之为 -- 系统编程。
驱动程序:所有的硬件,要想被软件访问,都需要自己的配套的驱动程序。如果没有驱动程序,这个硬件是访问不了的。而这些驱动程序,比如是在windows 当中,有些是windows自带的,有的需要自己安装,比如:有时候显卡的驱动程序就需要我们自己安装。
操作系统是如何管理 软硬件资源的?
首先要清楚的是,操作系统的用户是 被管理者,而 操作系统是 管理者,被管理者 和 管理者之间是不需要见面的;比如在大学当中,学生和校长之间不需要见面,校长也能够把学生管理好,可能大学四年,除了 新生典礼,毕业典礼,就没见过校长了。但是校长依然可以把学校管理好。
那么,接下来的问题就是,管理者在不见被管理者的情况下,他是如果对 用户进行管理的?
在学校当中,一个学生,旷了多少门课,挂了多少科,期末考试考了多少分,有没有被处分等等信息,校长如果想知道的话,是不需要直接去问学生的,通过学校对学生信息的保存记录就可以访问到学生的信息。所以,两者之间见不见面不是关键的,只要能够把信息管理起来,在未来就可以进行管理决策。通过对数据管理,达到对 人管理的目的。
但是,管理者有自己的事情要做,不可能每一次都自己去拿 被管理者的信息,所以,这时候就出现了辅导员,辅导员 本质上不属于管理者,因为 辅导员大多数情况都是在完成学校给的指标,任务,而 自己本身对于 学校的决策是不参与的。
校长可以通过辅导员来获取信息,比如学校要参加本市的编程大赛,找辅导员找出最优秀的 5 名学生参加比赛,这个名单最终是 辅导员交到 校长手上的,所以,辅导员更多的是 做 执行者角色。
在我们上述给出的 三种角色当中,对应 计算机当中的 操作系统 , 驱动程序 , 软硬件资源 的关系如下所示:
所以,操作系统入关管理好 软硬件资源,就需要获取到 软硬件的 各种状态数据,来进行管理;如何获取到 软硬件的资源呢? 驱动程序就可以获取到 硬件数据,把数据交给操作系统,操作系统就可以获取到 数据,对硬件进行管理;
比如 : 网卡出现了故障,此时,网卡发生故障的 状态信息级别 驱动程序所捕获,交给了 操作系统,如果操作系统能处理,那么就自己处理,如果不能处理,就会再向上反馈,反馈给用户,说网卡已故障。
操作系统对 软硬件的管理其实就只是对软硬件的 数据 的管理,但是,软硬件太多了,数据量太大 了,操作系统管理需要技巧。每一个硬件,都有他们公共的属性(都具有的属性),也有他们自己的属性,我们把一个 硬件的信息包装成一个结构体,那么,只需要一个表格(或者是数据结构),既可以对这些信息进行管理,那么在将来,只需要管理这个存储操作系统需要管理的结构体的结构体对象的数据结构,就可以实现对各个软硬件的管理了,比如:实现一个 查找所以没有运行的硬件的函数接口,操作系统想找到 没有运行的硬件,那么只需要 调用这个函数接口就可以找到没有运行的硬件。那么在操作系统当中,管理任何对象,最终都可以转化为对某种数据结构的增删查改操作。
像上述,给各个需要管理的对象包装成 结构体的过程,我们称之为 描述的过程;而 把这些结构体 用一个数据结构 进行管理的过程,我们称之为 组织的过程。
而,操作系统使用的这种思路,叫做 先描述在组织。
像上诉类似于 系统建立自己的数据结构来管理不同的对象,就是一个建模的过程。 为什么要建模的呢?因为计算机只能这么做,才能更好的管理的我们给的数据的,同时管理的本计算机的数据。
那么,对于操作系统,如果管理 软硬件资源,我们已经有答案了,就是先描述在组织。
所以,在操作系统当中,为了能更好的管理的软硬件资源,比如管理每一个硬件资源,都需要对这个硬件资源 的属性值,用相同的结构体来描述,不同的硬件之间还要加上属于自己的属性值。把这些 管理硬件资源的属性值都封装成一个对象,把 一个一个 对象包装到 某一个数据结构当中,这样的话,操作系统只需要管理各个数据结构就可以管理好 软硬件资源。
这也就以为着,操作系统当中有 各种各样,非常多的,不同类型的数据结构。
有了操作系统,当我们要访问某一个硬件资源的时候,就不能直接访问到 这个硬件,只能贯穿整个操作系统,逐一层级的来访问到 硬件。
不管是直接或者间接 访问到硬件的操作都要直接贯穿操作系统。
比如:C 当中的printf()函数,就要访问到显示器这个硬件进行输出,那么 C 当中实现的 printf()函数是在库当中实现的,那么 库对应的是 操作系统当中的 用户操作接口,那么 C 在实现这个结构的过程当中就要实现 system call 系统调用接口,通过 系统调用接口来调用到 更下一次层,逐步调用到 硬件。
所以,C 当中的 库函数 & 系统调用接口 之间的 关系是 上下层调用 和 被调用 的关系。
- 在开发角度,操作系统对外会表现为一个整体,但是会暴露自己的部分接口,供上层开发使用,这部分由操作系统提供的接口,叫做系统调用。
- 系统调用在使用上,功能比较基础,对用户的要求相对也比较高,所以,有心的开发者可以对部分系统调用进行适度封装,从而形成库,有了库,就很有利于更上层用户或者开发者进行二次开发。
操作系统 和 各个语言之间的练习
同样的,在编写代码当中,我们也使用的是先描述在组织的方式来写的,比如在 C 当中设计一个通讯录,都是先把一个成员(人)信息先描述好,再把这些信息以某种数据结构的方式来进行链接。在 C++ 当中也是类似,只不过结构体升级为了类,在类当中就不止有 成员属性了,还有这些成员属性的使用方法,我们把这些方法写成一个一个函数,这些函数就被称之为这个类的接口。
由这些函数,我们就可以把 对这成员属性 进行更好的管理,比如最简单的增删查改,甚至于,在不同的客户当中,编写出不同的函数,实现用户的不同需求。
你可以理解为,在C++ 当中,把本来在 C 当中分开的函数(属性使用方法) 和 属性包装在一起,这样 用户在使用的时候,就可以通过一个类构造出的对象来 管理这整个数据结构(各个对象的集合)。
在 C++ 当中 STL 当中实现的各种容器,不就是各个对象之间如果进行链接关系的组织关系吗?
在上述操作系统当中使用的 先描述在组织的方式是适用在操作系统当中的,我们的高级语言当中开发实现的各个容器,其实他本质上也只是把 操作系统当中的使用的 这种 先描述来组织的方式使用在了 容器的实现当中。
所以,在 C++ STL 当中就实现了各个容器来供我们使用,那么组织这点事情,C++ STL 库就把帮我们搞定了,我们只需要负责 把 各个对象当中的数据给描述好,然后对这个容器操作好,就可以了。
通过这种 先描述在组织的方式,就可以实现用计算机,描述过去发生的事情所产生的数据,从而通过数据结构进行链接,结果算法的计算,就可以组织出未来事件。
操作系统的加载
当我们的计算机关机的时候,那么就是直接关机了,但是在开机的时候,我们都知道,是要预加载操作系统的。
因为 cpu 在没有执行进程的时候,或则处于空闲状态时,cpu 会做一些日常性的 工作,比如:释放一些内存当中没用的,已经开辟的了空间;对操作系统当中需要加载到磁盘当中的数据,进行加载;检查当前计算机的状态;等等的操作。
所以,这就意味着,操作系统这个管理的软件,要想被cpu 给执行到,就必须加载到内存,也就说我们经常所示的启动操作系统。
在 冯诺依曼 当中,就是把 操作系统这 软件(程序),加载到 内存当中,变成一个进程。