汇编语言很难学,指令集很难记,mov/add/sub 等等系列的操作,到底为什么需要这样存在?为什么一定就是这几种指令? 本书当中在冯诺依曼结构里面给予了很好的解释;

在学习的过程当中,在所难免的会经常性的听说,我们现在的机器叫做冯诺依曼机。 本书第三章,把冯诺依曼结构与指令集一一对应,用冯诺依曼来区分指令集,避免死记硬背。 下面把这些东西的理解重新归纳总结一遍。

处理单元

计算机,顾名思义,首要功能就是计算,而运算也就对应着数学里面的基本操作: 加减乘除。 所以在 CPU 层面,实现了 add/sub 指令;对应的加法与减法,而乘法与除法,因为计算机存储用的都是二进制; 所以左移与右移也就对应的相应的乘法与除法;

控制单元

有了基本的运算之后,计算机并不满足于两个数据的加,减;想要更复杂的操作,就涉及到需要在一堆数据当中进行旋转跳跃;

假设有两个用户输入的值; 需要判断如果 a 大于 b 的情况下,我们就去操作 c 和 d。这样的需求,冯诺依曼里面的控制单元也就很好的满足了,cpu 的指令集层面,也对应实现了 cmp / jump 指令,而仅仅是跳转(jump)显然是不满足需求的,所以在指令集方面,也就有 jump when great 也就是 jg 指令, jump when above, ja 指令, jump when less, jl指令等等。

存储

在实现控制单元的时候,实际上还有一个问题并没有很好的去满足,那就是,我们运算的所有的东西,究竟是放在哪里的? 这个时候,也就引出了在冯诺依曼结构当中,存储的概念。 cpu 当中的寄存器/通俗概念当中内存/计算器的磁盘存储等等,都是内存。 而这个概念,对应的指令集当中的内容,就是寻址及数据搬迁。

譬如说,我计算了 a + b 的值,然后要和用户输入的一个值做比较,假设这个用户输入的值是从磁盘去读取出来的,然后写入到内存当中。那么我们也就需要从内存的某个位置读取,而找寻某个位置的地方也就简称为寻址; 寻址很重要,和现实世界当中的地址没什么区别;它可以分为两种情况: 绝对寻址/相对寻址 绝对寻址也就是获取到内存里面的绝对地址,譬如说内存条有 2^32 个地址,用十六进制表达,也就是 0x00000000 - 0xFFFFFFFF 而绝对寻址就是直接获取这个范围内存储的值。 而相对寻址,则是在当前程序运行的区域内进行寻址,譬如说: cmp eax ecx; jg 0x1234; 转换成自然语言,也就是比较 eax 与 ecx 内容的值,如果大于的情况下,跳转到当前地址加上 0x1234 的地址去;

而相对寻址的方式也是比较多的,可以参见我在binary-bomb实验当中简单总结的几种方法(x86);

关于数据搬迁,假设有用户输入的一个值,需要我们将其与 1/2/3/4/5 分别相加; 这个时候,我们就面临需要把用户输入的值移(搬迁)到某个地方(寄存器当中),然后将原有的值与1/2/3/4/5 分别相加,并且搬迁到某个地方分别进行存储; 这个动作,对应的是指令集当中的 mov 指令。

输入单元/输出单元

以上的内容, 定义了一台计算机的计算所需要的基本组件。 冯诺依曼结构当中的数据存储,用内存,找到数据,用寻址; 这个涉及到冯诺依曼结构当中的存储 冯诺依曼结构当中数据控制,用cmp,jump,在数据之间旋转跳跃; 冯诺依曼结构当中的数据计算,用 add / sub / shr / shl,对数据进行转换,变化;

这个涉及到计算内部状态的变化,这个里面缺了一块,就是与人的交互; 计算机最终需要为人服务,那么这就涉及到 输入: 人将自己的数据提供给到计算机 输出: 计算机将运算好的数据提供给到人

实际上,输入和输出的功能其实也是控制单元的一种特殊实现,譬如说,我们想获得键盘输入的内容,实际上就是键盘调用了某个程序,将键盘按下触发的信号存放到某个地方,然后我们再去读取它。不同的设备,也相应的有不同的驱动。

然而,就设备本身而言,除了电脑,有很多的输入设备,最普通的就是键盘鼠标,还有游戏手柄,笔;而现今的屏幕,即可以作为输出设备,也可以作为输入设备;

作为一名程序员来讲,如果需要知道如何去读键盘输入的内容,并且也知道如何控制显示器的数据,那编程的难度将大大增加。所以,操作系统为了简化这些输入输出的操作,特意的将这些操作打包成了一堆叫做服务程序的东西。而在 CPU 层面,使用 trap 进行调用这些服务程序。而作为程序员来说,所需要做的就是调用 trap 数字 来调用特定的程序,从而实现输入输出的目的; 这个也就是 os 里面所谓的系统调用;(书第九章)

以上,就是冯诺依曼结构里面和指令集的一些关系; 总而言之,冯诺依曼结构分为

  • control unit: cmp/jmp
  • process unit: add/sub/shr/shl
  • memory: mov
  • Input/Output: trap - (其实还有关于trap之后,栈的操作,现在的指令里面有很多叫做 call/ret,其实就是对这些东西的操作)