计算机组成原理(4):计算机的层次结构与工作原理

前言:

哈喽,大家好!

作为一名程序员,我们每天都在和计算机打交道。我们用 C++ 写算法,用 Java 写后端,用 Python 炼丹。我们敲下一行行代码,按下"Run"键,屏幕上就会神奇地蹦出我们要的结果。

但你有没有想过,在这个"黑盒子"内部,到底发生了什么?

  • 为什么 CPU 只认识 0 和 1,却能听懂你写的 print("Hello World")

  • 所谓的"虚拟机"真的只是指 VMware 吗?

  • 为什么计算机专业说的"透明",和我们日常理解的"透明"意思完全相反?

今天我将带领大家学习一下计算机系统的多级层次结构,已经带着大家从宏观的视角认识一下计算机系统的基本工作原理。

希望对大家有所帮助!


一、计算机的层次结构

计算机系统之所以强大,是因为它极度擅长"分工"与"抽象"。如果让一个写 Java 业务逻辑的程序员去关心底层的电容充放电,那互联网行业大概率要完蛋。

为了解耦,计算机系统被设计成了一个多级层次结构。每一层都是建立在下层基础之上的,同时又向上一层提供服务。这就像是一个精密的"俄罗斯套娃"。

第 1 、2层:微程序机器层(Micro-program Machine)、传统机器语言层(Machine Language)

这是最底层,也是最接近物理硬件的一层。

我们常说 CPU 执行指令,比如执行一条"取数指令"(LOAD)。在传统的机器语言层面,这只是一条指令。但在这一层,它被分解成了更细碎的步骤

微程序语言层:

  • 微指令/微操作:这是硬件能执行的最小动作。

  • 举个栗子: 假设有一条机器指令叫"把大象装进冰箱"。 在微程序层,它会被拆解为 3 个微指令:

    1. 打开冰箱门。
    2. 把大象塞进去。
    3. 关上冰箱门。

传统机器语言层:

  • 特征:它只能识别二进制指令(010101...)。

  • 用户:在这个层面上工作的,是极其早期的程序员,或者现在的固件工程师。

  • 痛点:人类直接读写二进制简直是反人类的。

之前我们学过这样的一个内容:

  • 我们用高级语言编写的代码,最终需要翻译成机器语言,才能被我们的CPU执行。比如这里我们之前说过是把C语言翻译成了这样的机器指令
  • 所以我们传统意义上的机器,它只能识别机器语言,那机器语言就是用二进制来表述的这种指令。
  • CPU在执行这些用二进制表示的机器指令的时候,还需要把这些机器指令细分为更细的一些小步骤来执行。
  • 那我们把这些更细分的小步骤把它称为微指令,或者也可以称为微操作。
  • 对于之前这个例子来说,第1条机器指令也就是这个取数的指令。
  • 而这个指令需要被划分为九个更细分的步骤,也就是九个微指令来依次的执行(分别是我们图中的九个步骤),才能完成取数这个操作。

我们可以把这儿的微程序机器看作是上层这个传统机器对这个实际机器的一个分解。

也就是用这个微程序机器的微指令来解释,并且执行m1这个传统机器的每一条机器指令。这就是计算机系统最底部的两个层次结构。


第 4 层:汇编语言层(Assembly Language)

这一层非常关键。为了解决二进制难以阅读的问题,20世纪50年代出现了汇编语言。

  • 本质:它是机器语言的"助记符"。

    • 机器指令:10100101 00000101 (这是啥?鬼知道)
    • 汇编指令:LOAD 5 (哦!是从内存地址 5 取数)
  • 一一对应 :除了宏指令外,每一条汇编指令几乎都严格对应一条机器指令

    • 红色操作码 LOAD 对应二进制的操作码部分。
    • 参数 5 对应二进制的地址码部分。

虚拟机器的概念** 对于汇编程序员来说,计算机看起来 好像能直接读懂汇编语言。但这是假象 ! 实际上,计算机根本不懂 LOAD 是什么。所有的汇编代码必须经过**汇编程序(Assembler)**翻译成二进制,机器才能执行。 所以,我们把这一层称为"虚拟机器"------它只是看起来存在,实际上是靠翻译器维持的幻觉。

那由于传统的机器只能识别这种0101的二进制指令。而这种二进制指令用来编程是很不方便的,所以在20世纪50年代开始出现了符号式的程序设计语言,也就是汇编语言。

对于使用汇编语言的程序员来说,他所看到的机器似乎是可以直接识别他所编写的汇编语言程序的。所以使用汇编语言的程序员所看到的机器,我们把它称为虚拟机器。

之所以叫虚拟机器,是因为其实任何一台机器都不可能直接的识别汇编语言。汇编语言编写的程序,想要执行,必须通过汇编程序的翻译,把它翻译为等价的机器语言指令,才可以执行。


第 5 层:高级语言层(High-level Language)

这就是我们熟悉的 C、Java、Python 所在的层次。

  • 特征:高度抽象,接近人类自然语言。

  • 屏蔽细节:程序员完全不需要关心寄存器、内存地址、操作码。你只需要关心业务逻辑。

  • 代价 :翻译过程最复杂。需要经过**编译(Compile)汇编(Assemble)**多个步骤才能变成机器码。

其实使用汇编语言来编写的这个程序只是更便于我们人类理解而已,但本质上它和机器语言的这种指令并没有太大的区别。

其依然是属于低级的语言,所以用这种汇编语言来编程,显然也是不方便的。

那随着计算机的发展,慢慢的出现了很多高级语言。我们现在编程大多都使用的是高级语言,比如说像c++,JAVA啊之类的,这些语言那么在我们这些高级语言程序员的视角看来,似乎我们所使用的这个机器,它就是可以直接识别高级语言的,但是其实并没有任何一台机器可以直接执行高级语言写的代码。

所以从我们的视角看到的这一台可以识别高级语言的机器,我们同样把它称为虚拟的机器。只是看起来就好像他能够懂高级语言一样啊,但事实上我们用高级语言编写的那些代码需要经过编译程序的翻译,先把它翻译成汇编语言描述的这种程序,然后再经过汇编语言程序翻译,才可以得到最终可以丢给机器执行的机器语言程序。

很多小伙伴可能会好奇,诶,第三层去哪里了,是不是我遗漏了,听我慢慢给大家道来:

第 3 层:操作系统层(Operating System)

从这一层开始,我们进入了"软件"的领域。

操作系统(OS)夹在硬件和应用程序之间。它扩充了机器的功能。

  • 广义指令 :我们写的程序经常需要读写文件、连接网络。这些操作硬件本身是很生硬的,但 OS 提供了一套系统调用(System Call)

  • 虚拟机器 :对于上层程序员来说,OS 屏蔽了底层硬件的复杂性。你不需要知道硬盘的磁头怎么转,你只需要调用 open() 函数。因此,这一层也被称为"操作系统虚拟机"。

对于这个层次结构的模型来说,每一个下层都是上层的基础。而每一个上层,又是对下层的一个拓展

随着高级语言的出现,我们所编写的这些程序难免会用到操作系统所提供的一些服务,比如说某一些系统调用。并且,一般来说,用汇编语言编写的程序同样也需要请求操作系统的服务,通过系统调用的方式来请求那系统调用,又可以称为广义指令

因此,我们把这个层次结构完善一下,我们应该在汇编语言机器的下方再插入一个操作系统机器。

那操作系统和操作系统之上的这些部分就属于软件的部分,而传统机器和下边这个微指令系统就属于硬件的部分。

值得一提的是,我们的ISA指令体系结构在软件与硬件的交界处,换句说法,就是它既不属于软件也不属于硬件


二、体系结构 vs 组成原理

很多计算机专业的同学,甚至跨考的同学,在复习时最容易搞混两个概念:计算机体系结构 (Computer Architecture)和计算机组成原理(Computer Organization)。

这两门课书名看着像,内容也重叠,到底区别在哪?

1. 区别的核心:设计 vs 实现

  • 计算机体系结构

    • 关注点有还是没有?(What)

    • 视角:机器语言程序员(底层程序员)看到的属性。

    • 例子:这台机器有没有"乘法指令"?如果有,指令格式是什么?

    • 类比 :这就像是汽车设计师 。他决定了这辆车得有方向盘、刹车、油门,方向盘是圆的。这是接口设计

  • 计算机组成原理

    • 关注点怎么实现?(How)

    • 视角:硬件逻辑设计人员看到的属性。

    • 例子:既然体系结构决定了有"乘法指令",那我是用一个专门的乘法电路去实现它(快但贵),还是用加法器多次累加来实现它(慢但省钱)?

    • 类比 :这就像是汽车工程师 。他负责把设计师画的方向盘连接到车轮上,是用液压助力还是电子助力?这是内部实现

2. 那个反直觉的术语:"透明性" (Transparency)

在日常生活中,我们说"某事公开透明",意思是看得见、摸得着、很清楚但在计算机科学中,"透明"的意思是:看不见!

举个神形象的例子: 看过《海贼王》吗?里面的阿布萨罗姆吃了透明果实 。 当他发动能力时,他变透明了。这意味什么?意味着你看不见他了

  • 技术语境 :如果说"具体的乘法电路实现对程序员是透明的",意思是程序员根本看不见也感知不到 底层到底是用了乘法器还是加法器。他只管写 a * b,底层怎么折腾,他完全不知道。

结论

  • 体系结构 研究的是那些不透明的部分(程序员必须知道有没有乘法指令,否则没法编程)。

  • 组成原理 研究的是那些透明的部分(底层怎么连线,程序员看不见,但硬件工程师得做)。


三、工作原理

了解了系统层次,我们首先来看一下我们编写的C语言程序是怎么一步一步转变成可执行文件的:

第 1 步:预处理 (Preprocessing)

那每一个刚开始学C语言的人,第一个程序肯定是写hello world,那我们写的这个程序是以.c后缀结尾的,我们把它称为源程序。

接下来,预处理器会对C语言当中以#号开头的那些命令进行处理。比如,我们是不是会在源程序的开头处定义一些常量,比如说常量#define π=3.1415926。

那定义了这个常量之后,我们在书写C语言程序的时候。是不是可以直接使用π这个常量?

预处理器所做的一个事情,就是会把我们宏定义的这些常量把它恢复成3.1415926。

我们在写程序的时候,使用宏定义的常量是为了让代码具有更高的可读性,但是对于编译器而言,需要把这些宏定义的常量把它恢复成原有的样子。只有恢复原样之后,才可以把这个原程序翻译成与之对等的汇编语言程序

  • 输入hello.c (源程序)

  • 工具 :预处理器 (cpp)

  • 动作 :处理所有以 # 开头的命令。

    • 比如你写了 #define PI 3.14

    • 在这一步,代码里所有的 PI 都会被暴力替换成 3.14

    • 注释会被删掉(机器不需要看注释)。

  • 结果 :一个纯净的、展开后的 C 代码文件(通常是以 .i 结尾,虽然你看不到)。

接下来我们需要把这个原程序翻译成与之对等的汇编语言程序,所以这一步就是编译器干的工作,这个我们在之前也介绍过。

第 2 步:编译 (Compilation)

  • 输入:预处理后的代码

  • 工具 :编译器 (cc1 / gcc)

  • 动作 :这是最难的一步。编译器要把高级语言翻译成汇编语言

    • 它会进行语法分析(你漏了分号就是在这报错)。

    • 它会进行代码优化。

  • 结果 :汇编代码文件(.s 文件)。此时文件里已经是 mov, add, push 这些汇编指令了。

那再往后,经过汇编器的处理,又会把汇编语言程序翻译成与之等价的机器语言程序。

由0101二进制组成,汇编器翻译之后得到的这个文件通常是以点o作为结尾,我们也把这类文件称为目标模块。

第 3 步:汇编 (Assembly)

  • 输入 :汇编代码 (.s)

  • 工具 :汇编器 (as)

  • 动作 :将汇编指令一对一地翻译成机器语言(二进制)。

  • 结果 :目标文件(.o 文件,Object File)。

    • 这时候文件里全是乱码(二进制)。

    • 关键点 :此时文件还不能运行!因为你调用的 printf 函数的代码还没放进来。

最后在hello world这个程序当中,我们除了自己写的代码之外,是不是也会调用一些标准库函数?

比如printf,那么为了生成最终可执行的文件,最后还需要经过链接器的处理,把相关的这几个目标模块链接成一个统一的可执行文件。

第 4 步:链接 (Linking)

  • 输入 :目标文件 (.o) + 标准库(如 C 标准库)

  • 工具 :链接器 (ld)

  • 动作

    • 你代码里调用的 printf,其实是在标准库里的。链接器会把你的代码和库函数的代码打包到一起。

    • 它还会解析地址,把大家整合到一个统一的地址空间。

  • 结果 :最终的可执行文件(Windows 下是 .exe,Linux 下是 a.out 或无后缀)。

  • 解决标准库函数等外部引⽤问题


四、计算机是如何跑起来的?

现在我们有了一个可执行文件,它静静地躺在你的硬盘 (外存)里。当我们双击它时,系统的工作原理开始运转。这里遵循的是经典的冯·诺依曼体系结构 ,也叫存储程序概念。

1. 加载 (Load)

程序不能直接在硬盘里跑,太慢了。 操作系统会把可执行文件从硬盘调入主存(内存)

这里之前我们也说过,就相当于那个C语言代码翻译成的指令调入内存中去。

2. 指令与数据同构

在内存里,你的代码(指令)和你的变量(数据)是没有区别的。它们都是一串串的二进制。

  • 计算机怎么知道哪个是指令,哪个是数据?

  • 全靠控制器的调度和程序的逻辑,或者说是调度过程的阶段不同。当然,通常指令会被放在代码段,数据放在数据段。

3. CPU 的"死循环":取指-执行周期

一旦程序进内存,CPU 就开始工作了。CPU 内部有一个极其重要的寄存器,叫 PC (Program Counter,程序计数器)

CPU 的工作流程就像一个不知疲倦的工人:

  1. 取指令 (Fetch)

    • CPU 看一眼 PC 里的值(比如地址 1000)。

    • 去内存地址 1000 的地方,把那条指令抓回来,放到指令寄存器(IR)里。

  2. PC 自增

    • PC 自动加 1(或者加指令长度),指向下一条指令(地址 1001)。这就保证了程序能一步步往下走。
  3. 分析指令 (Decode)

    • 控制器分析 IR 里的指令:哦,这是一条"加法"指令,要把寄存器 A 和 B 的值相加。
  4. 执行指令 (Execute)

    • 运算器(ALU)干活,算出结果。

    • 如果有需要,把结果写回内存或寄存器。

  5. 回到第 1 步

这个过程周而复始,快到每秒几十亿次(GHz),这就是我们计算机运行的本质。


总结

今天我们从宏观到微观,把计算机系统撸了一遍。 重点回顾一下:

  1. 五层结构:微程序 -> 机器语言 -> 操作系统 -> 汇编语言 -> 高级语言。

  2. 核心概念

    • 虚拟机器:软件构建的逻辑机器。

    • 透明性:本来存在但你看不见的东西(对程序员透明 = 程序员不用管)。

  3. 生成过程:预处理 -> 编译 -> 汇编 -> 链接。

  4. 运行原理:程序必须存入内存,CPU 通过 PC 指针不断取指执行。

本文的重点就是掌握几种概念,以及记住计算机的工作流程,还有层次结构。

相关推荐
Mr_sun.2 小时前
Docker中安装软件汇总(留存版)
运维·docker·容器
路边草随风2 小时前
llama_index简单使用
人工智能·python·llama
zqy02272 小时前
质量保障追求敏捷与快速交付
人工智能
YJlio2 小时前
Active Directory 工具学习笔记(10.9):AdInsight——命令行选项与自动化采集模板
笔记·学习·自动化
瀚岳-诸葛弩2 小时前
对比tensorflow,从0开始学pytorch(一)
人工智能·pytorch·tensorflow
古城小栈2 小时前
MCP协议 与 Function Call 的有点分不清楚
网络·网络协议
宝贝儿好2 小时前
【强化学习】第二章:老虎机问题、ε-greedy算法、指数移动平均
人工智能·python·算法
AI视觉网奇2 小时前
实时 数字人 DH_live 半身
人工智能·计算机视觉
美狐美颜SDK开放平台2 小时前
跨平台直播美颜SDK开发:iOS/Android/WebGL实现要点
android·人工智能·ios·美颜sdk·第三方美颜sdk·视频美颜sdk·美狐美颜sdk