前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >计算机总述--工作原理

计算机总述--工作原理

原创
作者头像
陈不成i
修改2021-05-25 17:46:50
修改2021-05-25 17:46:50
6200
举报
文章被收录于专栏:ops技术分享ops技术分享

cpu

cpu工作原理图:

在操作系统中,CPU被抽象成了时间片,而后将程序抽象成进程,通过分配时间片让程序运行起来。CPU有寻址单元用于来识别变量在内存的中所保存的集体内存地址。

而我们主机内部的总线是取决于CPU的位宽(也叫字长),比如32bit的地址总线,它能表示2的32次方个内存地址,转换成10进制就是4G内存空间。

内存

许多年以前,当人们还在使用DOS或是更古老的操作系统的时候,计算机的内存还非常小,一般都是以K为单位进行计算,程序也是几kb,运行程序直接放到内存中运行即可。

如今程序非常大,而内存很小,就此采用虚拟存储器来解决。虚拟存储器的基本思想是程序,数据,堆栈的总的大小可以超过物理存储器的大小,操作系统把当前使用的部分保留在内存中,而把其他未被使用的部分保存在磁盘上。比如对一个16MB的程序和一个内存只有4MB的机器,操作系统通过选择,可以决定各个时刻将哪4M的内容保留在内存中,并在需要时在内存和磁盘间交换程序片段,这样就可以把这个16M的程序运行在一个只具有4M内存机器上了。

大多数使用虚拟存储器的系统都使用一种称为分页(paging)机制。虚拟地址空间划分成称为页(page)的单位,而相应的物理地址空间也被进行划分,单位是页帧(frame).页和页帧的大小必须相同。

机器只有256M的物理地址,虚拟地址空间范围是00xFFFFFFFF(4G),而物理地址空间范围是0x000000000x0FFFFFFF(256M)。因此他可以运行4G的程序,但该程序不能一次性调入内存运行。这台机器必须有一个达到可以存放4G程序的外部存储器(例如磁盘或是FLASH),以保证程序片段在需要时可以被调用。在这个例子中,页的大小为4K,页帧大小与页相同——这点是必须保证的,因为内存和外围存储器之间的传输总是以页为单位的。对应4G的虚拟地址和256M的物理存储器,他们分别包含了1M个页和64K个页帧。

存储

由于CPU内部的寄存器存储的空间有限,于是就用了内存来存储数据,但是CPU和速度和内存的速度完全不在一个档次上,因此在处理的数据的时候回到多数都在等(CPU要在内存中取一个数据,cpu转一圈的时间就可以处理完,内存可能是需要转20圈)。为了解决使得效率更加提高,就出现了缓存这个概念。

既然我们知道了程序的局部性原理,有知道了CPU为了获得更多的空间其实就是用时间去换空间,但是缓存就是可以直接让cpu拿到数据,节省了时间,所以说缓存就是用空间去换时间

数据存储速度和结构:

CPU在处理数据的地方就是在寄存器中修改,当寄存器没有要找的数据是,就会去一级缓存找,如果一级缓存中没有数据就会去二级缓存中找,依次查找知道从磁盘中找到,然后在加载到寄存器中。当三级缓存从内存中取数据发现三级缓存不足时,就会自动清理三级缓存的空间。

我们知道数据最终存放的位置是硬盘,这个存取过程是由操作系统来完成的。而我们CPU在处理数据是通过两种写入方式将数据写到不同的地方。

那就是通写(写到内存中)和回写(写到一级缓存中)。很显然回写的性能好,但是如果断电的话就尴尬了,数据会丢失,因为他直接写到一级缓存中就完事了,但是一级缓存其他cpu核心是访问不到的,因此从可靠性的角度上来说通写方式会更靠谱。具体采用哪种方式得你自己按需而定啦。

多级缓存

1、一级缓存基本上都是内置在cpu的内部和cpu一个速度进行运行,能有效的提升cpu的工作效率。一级缓存越多,cpu的工作效率就会越来越高,是cpu的内部结构限制了一级缓存的容量大小,使一级缓存的容量都是很小的。

2、二级缓存主要作用是协调一级缓存和内存之间的工作效率。cpu首先用的是一级内存,当cpu的速度慢慢提升之后,一级缓存就不够cpu的使用量了,这就需要用到二级缓存。

3、三级缓存和一级缓存与二级缓存的关系差不多,是为了在读取二级缓存不够用的时候而设计的一种缓存手段,在有三级缓存cpu之中,只有大约百分之五的数据需要在内存中调取使用,这能提升cpu不少的效率,从而cpu能够高速的工作。

其实三级缓存就是就是多颗CPU共享的空间。当然多颗cpu也是共享内存的。将三级缓存或者内存进行分段,每段给对应的cpu使用。

硬件设备

设备组成:

  1. 设备控制器,集成在主板的一块芯片活一组芯片。负责从操作系统接收命令,并完成命令的执行,比如负责从操作系统中读取数据。
  2. 设备本身,有自己的接口,但需要连接使用。

每个控制器都有少量的用于通信的寄存器(几个到几十个不等)。这个寄存器是直接集成到设备控制器内部的。比方说,一个最小化的磁盘控制器,它也会用于指定磁盘地址,扇区计数,读写方向等相关操作请求的寄存器。所以任何时候想要激活控制器,设备驱动程序从操作系统中接收操作指令,然后将它转换成对应设备的基本操作,并把操作请求放置在寄存器中才能完成操作的。每个寄存器表现为一个IO端口。所有的寄存器组合称为设备的I/O地址空间,也叫I/O端口空间,

驱动程序: 真正的硬件操作是由驱动程序操作完成的。驱动程序通常应该由设备生产上完成,通常驱动程序位于内核中,虽然驱动程序可以在内核外运行,但是很少有人这么玩,因为它太低效率啦!

实现输入和输出: 设备的I/O端口没法事前分配,因为各个主板的型号不一致,所以我们需要做到动态指定。电脑在开机的时候,每个IO设备都要想总线的I/o端口空间注册使用I/O端口。这个动态端口是由所有的寄存器组合成为设备的I/O地址空间,有2^16次方个端口,即65535个端口。

三种方式实现I/O设备的输入和输出: 轮询: 通常指的是用户程序发起一个系统调用,内核将其翻译成一个内核对应驱动的过程调用,然后设备驱动程序启动I/O,并在一个连续循环不断中检查该设备,并看该设备是否完成了工作。这有点类似于忙等待(就是cpu会用固定周期不断通过遍历的方式去查看每一个I/O设备去查看是否有数据, 显然这种效率并不理想。)

中断: 中断CPU正在处理的程序,中断CPU正在执行的操作,从而通知内核来获取中断请求。在我们的主板通常有一个独特的设备,叫做可编程中断控制器。这个中断控制器可以通过某个针脚和CPU直接进行通信,能够出发CPU发生某个位置偏转,进而让CPU知道某个信号到达。中断控制器上会有一个中断向量(我们每一个I/O设备在启动时,要想中断控制器注册一个中断号,这个号通常是唯一的。通常中断向量的每一个针脚都是可以识别多个中断号的),也可以叫中断号。

因此当这个设备真正发生中断时,这个设备不会把数据直接放到总线上,这个设备会立即向中断控制器发出中断请求,中断控制器通过中断向量识别这个请求是哪个设备发来的,然后通过某种方式通知给CPU,让CPU知道具体哪个设备中断求情到达了。这个时候CPU可以根据设备注册使用I/O端口号,从而就能获取到设备的数据了。(注意,CPU是不能直接取数据的哟,因为他只是接收到了中断信号,它只能通知内核,让内核自己运行在CPU上,由内核来获取中断请求。)举个例子,一个网卡接收到外来IP的请求,网卡也有自己的缓存区,CPU讲网卡中的缓存拿到内存中进行去读,先判断是不是自己的IP,如果是就开始拆报文,最后会获取到一个端口号,然后CPIU在自己的中断控制器去找这个端口,并做相应的处理。

内核中断处理分为两步:中断上半部分(立即处理)和中断下半部分(不一定)。还是从网卡接收数据为例,当用户请求到达网卡时,CPU会命令讲网卡缓存区的数据直接拿到内存中来,也就是接收到数据后会立即处理(此处的处理就是将网卡的数据读到内存中而已,不做下一步处理,以方便以后处理的。),这个我们称之为中断的上半部分,而后来真正来处理这个请求的叫做下半部份

DMA: 直接内存访问,大家都知道数据的传输都是在总线上实现的,CPU是控制总线的使用者,在某一时刻到底是有哪个I/O设备使用总线是由CPU的控制器来决定的。总线有三个功能分别是:地址总线(完成对设备的寻址功能),控制总线(控制各个设备地址使用总线的功能)以及数据总线(实现数据传输)。

通常是I/O设备自带的一个具有智能型的控制芯片(我们称之为直接内存访问控制器),当需要处理中断上半部分时,CPU会告知DMA设备,接下来总线归DMA设备使用,并且告知其可以使用的内存空间,用于将I/O设备的数据读取到内存空间中去。当DMA的I/O设备将数据读取完成后,会发送消息告诉CPU以及完成了读取操作,这个时候CPU再回通知内核数据已经加载完毕,具体中断下半部分的处理就来交个内核处理了。现在大多数设备都是用DMA控制器的,比如:网卡,硬盘等等

总图:

我们的CPU有要想跟指定设备打交道,就需要把指令传给驱动,然后驱动讲CPU的指令转换成设备能理解的信号放在寄存器中(也可以叫套接字,socket).所以说寄存器(I/O端口)是CPU通过总线和设备打交道的地址(I/O端口)。

进程

程序跑起来,我们统一叫进程(我们暂时不用理会线程)。那如果多个进程同时运行就意味着把这些有限的抽象资源(cpu,memory等等)分配给多个进程。我们把这些抽象资源统称为资源集。

资源集包括: 1>.cpu时间; 2>.内存地址:抽象成虚拟地址空间(如32位操作系统,支持4G空间,内核占用1G空间,进程也会默认自己有3G可用,事实上未必有3G空间,因为你的电脑可能会是小于4G的内存。) 3>.I/O:一切皆文件打开的多个文件,通过fd(文件描述符,file descriptor)打开指定的文件。我们把文件分为三类:正常文件、设备文件、管道文件。

每一个进行都有自己作业地址结构,即:task struct。其就是内核为每个进程维护的一个数据结构(一个数据结构就是用来保存数据的,说白了就是内存空间,记录着该进程所拥有的资源集,当然还有它的父进程,保存现场【用于进程切换】,内存映射等待)。task struct模拟出来了线性地址,让进程去使用这些线性地址,但是它会记录着线性地址和物理内存地址的映射关系的。

内存映射

只要不是内核使用的物理内存空间我们称之为用户空间。内核会吧用户空间的物理内存切割成固定大小的页框(即page frame),就是一个固定大小的存储单位,比默认的单个存储单元(默认是一个字节,即8bit)要大.通常每4k一个存储单位。每一个页框作为一个独立的单元向外进行分配,且每一个页框也都其编号。【举个例子:假设有4G空间可用,每一个页框是4K,一共有1M个页框。】这些页框要分配给不同的进程使用。

我们假设你有4G内存,操作系统占用了1个G,剩余的3G物理内存分配给用户空间使用。每一进程启动之后,都会认为自己有3G空间可用,但是实际上它压根就用不完3G。进程进行写入内存是被离散存储的。哪有空余内存就往哪存取。具体的存取算法不要问我,我也没有研究过。

进程空间结构: 1>.预留空间 2>.栈(变量存放处) 3>.共享库 4>.堆(打开一个文件,文件中的数据流存放处) 5>.数据段(全局的静态变量存放处) 6>.代码段

进程和内存的存储关系如下:

每个进程空间都有预留空间,当某个进程发现自己打开的数据已经不够用,它需要打开一个新文件(打开一个新文件就需要在进程的地址空间存放数据),很显然我们上图的进程地址空间是线性的并不是真正意义上的。当一个进程真正去申请使用一个内存时,需要向内核发起系统调用,由内核在物理内存上找一个物理空间,并告诉该进程可以使用的内存地址。比方说进程要在堆上打开一个文件,它需要向操作系统(内核)申请使用内存空间,且在物理内存允许的范围内(即请求的内存需要小于空闲物理内存),内核会分配给该进程内存地址。

每一进程都有自己线性地址,这个地址是操作系统虚拟出来的,并不真实存在,它需要把这个虚拟地址和真正的物理内存做一个映射关系,如图“进程和内存的存储关系”,最终的进程数据的存放处位置还是映射到内存中了。这就意味着,当一个进行跑到CPU上执行时,它告诉CPU的是自己的线性地址,这时候CPU不会直接去找这个线性地址(因为线性地址是虚拟出来的,不真实存在,真正存放地址进程的是物理内存地址。),它会先去找这歌进程的“task struct”,并装载页表(page table)[记录着线性地址到物理内存的映射关系,每一个对应关系叫做一个页表项。],以读取到进程的所拥有的线性地址所对应的真正的物理内存地址。

CPU访问进程的地址时,首先获取到的是进程的线性地址,它将这个线性地址交给自己的芯片MMU进行计算,得到真正的物理内存地址,从而达到访问进程内存地址的目的。换句话说,只要他想要访问一个进程的内存地址,就必须经过MMU运算,这样导致效率很低,因此他们有引进了一个缓存,用于存放频繁访问的数据,这样就可以提高效率,不用MMU进行计算,直接拿到数据去处理就OK了,这个缓存器我们称之为:TLB:转换后援缓冲器(缓存页表的查询结果)

注意:在32bit的操作系统是线线地址到物理内存的映射。而在64bit操作系统是恰恰相反的!

用户态和内核态

操作系统运行时为了呢能够实现协调多任务,操作系统被分割成了2段,其中接近于硬件一段具有特权权限的叫做内核空间,而进程运行在用户空间当中。所以说,应用程序需要使用特权指令或是要访问硬件资源时需要系统调用。

只要是被开发成应用程序的,不是作为操作系统本身的一部分而存在的,我们称之为用户空间的程序。他们运行状态称之为用户态。

需要在内核(我们可以认为是操作系统)空间运行的程序,我们称之他们运行在内核空间,他们运行的状态为用户态,也叫核心态。注意:内核不负责完成具体工作。在内核空间可用执行任何特权操作。

每一个程序要想真正运行起来,它最终是向内核发起系统调用来完成的,或者有一部分的程序不需要内核的参与,有我们的应用程序就能完成。我们打个比方,你要计算2的32次方的结果,是否需要运行在内核态呢?答案是否定的,我们知道内核是不负责完成具体工作的,我们只是想要计算一个运算结果,也不需要调用任何的特权模式,因此,如果你写了一些关于计算数值的代码,只需要把这个代码交给CPU运行就可以了。

如果一个应用程序需要调用内核的功能而不是用户程序的功能的话,应用程序会发现自己需要做一个特权操作,而应用程序自身没有这个能力,应用程序会向内核发申请,让内核帮忙完成特权操作。内核发现应用程序是有权限使用特权指令的,内核会运行这些特权指令并把执行结果返回给应用程序,然后这个应用程序拿到特权指令的执行结果后,继续后续的代码。这就是模式转换。

因此一个程序员想要让你的程序具有生产力,就应该尽量让你的代码运行在用户空间,如果你的代码大多数都运行在内核空间的话,估计你的应用程序并不会给你打来太大的生产力哟。因为我们知道内核空间不负责产生生产力。

而内核调用转换的时候需要切换上下文。将用户态指令保存,再运行内核态指令。执行完毕后,需要再回复到用户态,也就是2次上下文切换,不会涉及虚拟内存等资源,也不会切换进程,只是进程中指令在环中的切换。

我们知道计算机的运行就是运行指令的。指令还分特权指令级别和非特权指令级别。了解过计算机的朋友可能知道X86的CPU架构大概分成了四个层次,由内之外共有四个环,被称为环0,环1,环2,环3。我们知道环0的都是特权指令,环3的都是用户指令。一般来讲,特权指令级别是指操作硬件,控制总线等等。

一个程序的执行,需要在内核的协调下,有可能在用户态和内核态互相切换,所以说一个程序的执行,一定是内核调度它到CPU上去执行的 。有些应用程序是操作系统运行过程当中,为了完成基本功能而运行的,我们就让他在后台自动运行,这叫守护进程。但是有的程序是用户需要的时候才运行的,那如何通知内核讲我们需要的应用程序运行起来呢?这个时候你就需要一个解释器,它能和操作系统打交道,能够发起指令的执行。说白了就是能够把用户需要的运行请求提交给内核,进而内核给它开放其运行所需要的有赖于的基本条件。从而程序就执行起来了。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • cpu
  • 内存
  • 存储
  • 多级缓存
  • 硬件设备
  • 进程
  • 内存映射
  • 用户态和内核态
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档