我将简要描述一个模拟器来解释问题中提到的主要概念。
假设我正在使用视频游戏模拟器 Mame,并选择旧的经典街机“Miss PacMan”。查看原理图或直接查看街机视频游戏内的 PCB,很容易找到处理器:zilog Z80,唯一的 40 针大芯片。现在,如果我们获得该处理器的技术数据,我们可以找到它可以执行的每条指令的二进制编码。基本上,它得到一个 8 位数据(值范围从 0 到 255),告诉处理器要做什么。在仿真器的情况下,它读取字节(与原始 Miss pac-man 电子板内的 Z80 处理器完全相同的字节),确定 Z80 会做什么并模拟指令。
一些经典的视频游戏可能使用 x86 处理器,类似于目前大多数 PC 中使用的处理器。即使在 Mame 中选择这样的游戏,模拟器仍然会读取在该游戏中找到的字节,并以 x86 处理器的方式解释每个字节。换句话说,模拟器不会利用 PC 和模拟游戏使用类似处理器的事实。无论运行 Mame 的 PC 是否与原始游戏有任何相似之处,它都会执行相同的步骤来模拟任何游戏。
您在问解释器如何执行代码?解释器是一个程序(解释器只是一个软件,而不是物理处理器)。措辞实际上令人困惑。为了使这句话有意义,我们需要以下所有条件:
1 - 要解释的程序已经是二进制的,可以直接由您 PC 中使用的处理器执行的机器语言
2 - 程序位置,使用的确切地址,与您可以在 PC 中保留的位置相同
3 - 任何库和任何 I/O 占用完全相同的地址
当所有这些条件都满足时,解释器可以告诉你 PC 上的处理器停止执行来自解释器的代码,而是“跳转”到要解释的程序的代码中。任何人都可以说:它不是解释器,它只是一个启动器。
也许这样一个实际上不解释但让你的处理器完成真正工作的解释器在以下方面仍然有用:它可以让你的处理器执行一些工作,但在代码执行时请求生成异常被解释正在执行某种类型的指令。例如,让代码运行,但在尝试执行“IN”或“OUT”的任何变体时会生成“一般保护错误”或“陷阱”或“异常”。解释器会记下正在写入的 I/O 端口,或者它会选择一个值来提供而不是允许读取真正的 I/O 端口。然后解释器会设法让处理器在程序中“跳转”以在指令“IN”或“OUT”之后的位置进行解释。
通常,解释器读取 ASCII 文本文件、原始源代码(可能是 Unicode 而不是 ASCII),逐行、逐字确定编译器会做什么,然后即时模拟任务。当原始编译器需要读取很多行以完全理解当前任务时,解释器也需要读取所有这些行才能模拟相同的任务。
解释器的一大优势是它不会崩溃。因为每条指令都是模拟的,所以它对任何错误或恶意代码都不敏感。在计算机遇到任何错误后需要重新启动的时候,在重新启动需要 10 分钟或更长时间的时候,这是一个很大的优势。
如今,快速 SSD 可在 5 秒内重新启动,并且可靠的操作系统可以捕获一个进程中的任何错误并关闭该进程而不影响机器的稳定性,因此选择慢速解释器而不是多更快的 JIT 或更快的二进制可执行文件