前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >打开操作系统的大门,这篇就够了

打开操作系统的大门,这篇就够了

作者头像
会玩code
发布于 2022-04-24 06:03:18
发布于 2022-04-24 06:03:18
7400
举报
文章被收录于专栏:会玩code会玩code

一切的起源 图灵机

图灵机主要由数据存储单元,控制单元,运算单元和一个可读写外部数据的读写头几部分构成。

图灵机工作需要有一条纸带,纸带上面布满格子,可以在格子上面记录字符,字符可分为数据字符和指令字符;纸带穿过图灵机并不断向前移动;图灵机上的读写头依次读取纸带格子上的字符,根据控制单元区分读取的字符属于数据还是指令,当读到数据字符时,将字符存储到存储单元中,当读到指令字符时,运算单元会将存储单元中的数据读取出来并进行相应运算,并将结果通过读写头写入纸带的下一个格子中。

图灵机的基本工作模式跟如今的计算机是一样的,数据和指令存在存储器中(纸带和存储单元),处理器读取后运算得出结果。计算机中使用cpu进行指令计算,存放数据的存储器我们常听的有磁盘、SSD、内存。但cpu并不直接从这些存储器中读取并执行指令,而是采用分级缓存策略。

存储器分级

为什么需要分级

我们比较熟悉的磁盘,数据在断电之后还能保存着,而且磁盘的存储空间较大,通常能有上T容量,但其数据读取速度极慢;内存的读取速度虽然比磁盘快了将近100倍,但跟cpu的执行速度相比,还是属于”龟速“。此外,内存是安装在主板上的,数据通过电路板传输到cpu上,数据传输的耗时相对于cpu执行速度来说也是不可忽视的。

存储器体积越小,其存储容量就会受到限制;读写速度越快,能耗和成本也会越高;其次,存储器距离cpu越远,数据传输也越慢。所以,目前而言,使用单一存储器无法让存储器中的数据跟的上cpu的处理速度。

计算机采用的方案是将存储器分级,将cpu使用频率越高的数据,存放在读写速度越快,距离cpu更近的存储器(缓存)中;将使用频率较低的数据存放在读写速度较慢,距离cpu较远,但存储容量较大,成本较低的存储器中。这样,cpu读取数据时,直接先从缓存中读取,缓存中不存在再从距离更远的存储器中读取。

分级缓存方案的可行性在于计算机存在局部性原理,试想下我们平时写的代码程序,运算用的最多的是for循环,然后对定义的几个变量进行计算读写。所以,cpu执行一个程序的时候,有几个数据区域的读写频率是比较高的。所以,可以将这些「热点」区域的数据缓存起来,下次读取的时候就会快很多。据统计,存储器缓存命中率能达到95%,也就是只有5%的数据会穿透到内存。

存储器分级策略

通常,存储器分成以下几个级别:

  • 寄存器
  • CPU cache
    • L1-cache
    • L2-cache
    • L3-cache
  • 内存
  • 磁盘/SSD

磁盘/SSD

SSD/磁盘是距离CPU最远,读取速度最慢的一类存储器,优点在于成本较低,断电后数据还在。其中SSD是我们常说的固态硬盘,结构与内存类似,读写速度比内存慢10-1000倍;磁盘读取速度更慢,比内存慢100W倍左右,随着SSD的普及,已经慢慢被取代了。

内存

内存是插在主板上,与CPU有一段距离,CPU通过主板总线读取内存中的数据,造价比磁盘稍贵,但读取速度比磁盘快,速度大概在200-300个CPU周期;容量方面,个人电脑的内存一般是8-16G,服务器上的内存可以达到几个T。

CPU周期:一条指令可分为取指令,执行指令等若干个阶段,每个阶段完成所需的时间成为CPU周期。

CPU cache(CPU 高速缓存)

CPU cache存在于CPU内部,CPU cache可分为L1(一级缓存)、L2(二级缓存)、L3(三级缓存),CPU的每个核都有各自的L1和L2缓存,同一个CPU的多个核共享一个L3缓存。

与CPU距离:L1 < L2 < L3

容量: L1(几十~几百KB)< L2(几百KB~几MB) < L3(几MB~几十MB)

读写速度: L1(2-4CPU周期) > L2(10-20CPU周期) > L3(20-60CPU周期)

(L1缓存划分了指令区和数据区,下文会解释)

需要注意的是,cpu缓存中每个缓存的最小单位是内存的一个内存块,而不是缓存一个变量;cpu缓存和内存的映射方式有很多种,类似于 cache 行号 = 内存页号 mod cache 总行数;这样,先根据内存地址计算出地址所在内存页号,再通过映射关系算出cache行号,如果存在缓存中,直接获取数据即可,如果不存在再到内存中获取。

寄存器

寄存器是CPU实际进行指令读写的地方,是距离CPU最近的存储器,读写速度也是最快,能在半个CPU周期完成读写;一个CPU中寄存器数量在几十到几百个之间,每个寄存器容量很小,只能存储一定字节(4-8个字节)的数据。

32 位 CPU 中大多数寄存器可以存储 4 个字节 64 位 CPU 中大多数寄存器可以存储 8 个字节

寄存器根据用途不同,可分为好几类,为了便于后面指令执行过程学习,我们先了解以下几类:

  • 通用寄存器:用于存储程序参数数据。
  • 指令寄存器:每条CPU执行的指令,会先从内存中读入指令寄存器中,然后再让CPU读取执行。
  • 指令指针寄存器:存放着CPU下一条要执行的指令所在的内存地址,CPU根据指令指针寄存器中的指令内存地址,将指令读入指令寄存器中。指令指针寄存器也成为IP寄存器。
  • 段寄存器:为了可访问更大的物理空间,CPU通过基础地址+偏移量定位一个物理内存地址。段寄存器中存储的是基地址信息。CS是存放指令地址的一个段寄存器,与IP寄存器一起定位指令在内存中的地址。

假设一个寄存器最大存储4字节数据,4字节=4*8=32位,值表示范围:0~(2^32) -1,换算单位为4G,也就是这个寄存器最大能查找0-4G范围的地址,但我们之前提到的内存容量可达几T,所以,直接通过一个寄存器无法表示全部范围的内存地址。 采用“基础地址+偏移地址=物理地址”的寻址模式,可极大扩大内存寻址能力。例如:32位的基础地址左移32位,再加上32位的偏移地址,可表示64位(16EiB)的内存地址。 需要注意的是,计算机的最终寻址范围是由下面介绍的地址总线决定的。

总线 - CPU与外界的桥梁

按上面的存储器分级,数据先从磁盘加载到内存中,然后被读取到CPU内部的高速缓存和寄存器中,CPU读取寄存器进行处理。其中,CPU和CPU cache之间的数据读写是在CPU内部中完成的,CPU对内存的读写则是通过主板上的总线完成的。

总线可以看成是多根导线的集合,通过控制导线电压的高低来传递数据,高电压是1,低电压是0。

根据传输信息的不同,总线分为地址总线,数据总线和控制总线

试想“向内存3位置读取数据”这一条读指令包含了几个信息:

  • 操作的内存位置是3(地址信息)
  • 操作的命令是读命令(控制信息)
  • 数据传输(数据信息)

3类总线分别负责对应信息的传输:CPU通过地址总线将要操作的内存地址信息传递给内存;通过控制总线发出内存读命令;内存将数据从数据总线传入CPU。

图片源自《汇编语言(第3版)》

地址总线

讲地址总线之前,我们先讲讲存储器地址的划分。存储器会被划分为若干个存储单元,存储单元从零开始编号,这些编号可以看做是存储单元在存储器中的地址。

每个存储单元由8个位(bit)组成,也就是可以存储一个字节的数据;假设一个存储器有128个存储单元,可以存储128个字节(Byte)。

CPU通过地址总线来指定存储单元,地址总线的线数,决定了对内存的寻址范围。比如,一个CPU有16根地址总线,最多可以寻找2的16次方个内存单元。

假设一个16位的CPU有20条地址总线,16位的CPU如何一次性给出20位的地址呢?

其实答案前面已经给出了,CPU内部会通过「基础地址」+「偏移地址」的方法合成一个20位的地址。

图片源自《汇编语言(第3版)》

数据总线

CPU与内存或其他器件通过数据总线进行数据传输,数据总线的宽度(总线条数)决定了CPU与外界的数据传输速度。8根数据总线一次可传输一个字节(8bit)的数据,16根数据总线一次可传输两个字节(16bit)。

控制总线

CPU对外部器件的控制是通过控制总线来进行的,多少根控制总线,意味着CPU对外部器件有多少种控制,所以控制总线的宽度决定了CPU对外部器件的控制能力。

指令执行

了解了各种存储器和总线,我们再来看看程序是如何从磁盘加载到内存然后被CPU执行的。

我们编写的程序需要先经过编译器翻译成CPU认识的指令,这个过程称为指令的构造。程序启动时,会将程序的指令和数据分别存在两个内存段中。同时,PC指针(IP寄存器+CS寄存器)会指到指令段的起始地址(就是将起始地址赋值到PC指针上),表示CPU将从这个地址开始读取内存中的指令并执行。

指令解析

指令先被读取到指令寄存器中,CPU取出执行时,需要先对指令进行解析。

我们都知道,内存中存放的内容都是二进制类型(上面的指令我们写成16进制),cpu读取到要执行的指令后,会先对二进制的指令进行解析。以上面“0x8c400104”为例,拆分成二进制:

上面指令分成操作码、寄存器编号、内存地址三部分:

  • 最左边6位,称为操作码,“10011”表示load指令。
  • 中间4位,指定了寄存器的编号,“0001”表示R1寄存器。
  • 最后的22位表示要操作的内存地址。

所以,这条指令是指将指定内存地址的内容加载到寄存器R1中。

总结一下,程序执行时:

  1. 程序的指令和数据分别存入内存指令段和数据段中,PC指针指到指令段的起始地址。
  2. CPU读取PC指针指向的指令存入指令寄存器中。
    1. CPU通过地址总线指定要访问的内存地址;通过控制总线发送“读指令”。
    2. 内存通过数据总线将数据传入CPU,CPU将这个数据存到指令寄存器中。
  3. CPU解析指令寄存器中的指令内容。
  4. CPU通过运算单元和控制单元执行指令。
  5. PC指针自增,指向下一条指令内存地址。

所以,取址、译码、执行,这是一个指令的执行周期,所有指令都会严格按照这个顺序执行

指令预读

CPU执行指令的速度是非常快的,但内存的读写是非常慢的,所以,如果从内存中一条条读取指令再执行的话,指令执行周期会变得很慢。

前面我们学到,CPU内部还有三级缓存,所以,我们可以将内存中的多条指令一次性先读到读写速度较快的L1缓存中,这样,取址速度就能跟的上CPU的指令执行速度了。

同时,为了避免数据缓存覆盖指令缓存影响指令执行,我们可以将L1缓存划分为指令区和数据区。

思考下L2和L3需要划分指令区和数据区吗?其实是不需要的,因为L2和L3并不需要协助指令预读。

如何更快的执行指令

为了更快的执行指令,我们需要使用CPU的指令流水线技术。

在刚才的流程中,取指,解码的时候运算单元是空闲的,为了提高指令处理速度,需要让运算单元就可以一直处于运算中。我们可以使用CPU的指令流水线技术,在第一条指令完成取址进行译码时,第二条指令立刻进行取址,依次类推,这样,在上一条指令完成执行后,下一条指令也完成译码可以进行执行了。

图片源自网络

一句话总结

  1. 程序存储在存储器中,cpu读取指令并进行执行计算。
  2. 由于cpu的指令执行速度极快,目前没有存储器能同时满足读写速度快,散热小,能耗低,容量大等要求,所以采用存储器分级策略,使用多级缓存来匹配上cpu的执行速度。
  3. cpu与内存之间的数据传输通过主板上的总线来完成。通过地址总线将要操作的内存地址信息传递给内存;通过控制总线发出命令类型;内存将数据从数据总线传入CPU。
  4. 寄存器是cpu直接读取指令和参数数据的存储器。寄存器按用途可分为好几类。对于数据,会先将数据读到通用寄存器中,之后CPU从通用寄存器中读写数据;对于指令,CPU会先根据CS段寄存器和指令指针寄存器共同指向的指令内存地址获取指令,并将指令存入指令寄存器中,之后CPU再从指令寄存器中读取执行。
  5. 指令的执行包括取址、译码、执行。为了避免CPU每次获取指令都得从内存中获取,可以先将指令预读到CPU L1-Cache中;同时,为了让CPU的计算单元一直处于运算状态,可以使用流水线技术。
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-02-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 会玩code 微信公众号,前往查看

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

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档