在开始愉快的PA之旅之前
PA的目的是要实现NEMU, 一款经过简化的x86全系统模拟器. 但什么是模拟器呢?
你小时候应该玩过红白机, 超级玛丽, 坦克大战, 魂斗罗... 它们的画面是否让你记忆犹新? (希望我们之间没有代沟...) 随着时代的发展, 你已经很难在市场上看到红白机的身影了. 当你正在为此感到苦恼的时候, 模拟器的横空出世唤醒了你心中尘封已久的童年回忆. 红白机模拟器可以为你模拟出红白机的所有功能. 有了它, 你就好像有了一个真正的红白机, 可以玩你最喜欢的红白机游戏. 这里是jyy移植的一个小型项目LiteNES, PA工程里面已经带有这个项目, 你可以在如今这个红白机难以寻觅的时代, 再次回味你儿时的快乐时光, 这实在是太神奇了!
Docker container中默认并不带有GUI, 为了运行LiteNES, 你需要根据主机操作系统的类型, 你需要下载不同的X Server:
- Windows用户. 点击这里下载, 安装并打开Xming.
- Mac用户. 点击这里进入XQuartz工程网站, 下载, 安装并打开XQuartz.
- GNU/Linux用户. 系统中已经自带X Server, 你不需要额外下载.
然后根据主机操作系统的类型, 为SSH打开X11转发功能:
- Mac用户和GNU/Linux用户. 在运行
ssh
时加入-X
选项即可:ssh -X -p 20022 [email protected]
- Windows用户. 在使用
PuTTY
登陆时, 在PuTTY Configuration
窗口左侧的目录中选择Connection -> SSH -> X11
, 在右侧勾选Enable X11 forwarding
, 然后登陆即可.
通过带有X11转发功能的SSH登陆后, 在nexus-am/apps/litenes
目录下执行make run
,
即可在弹出的新窗口中运行基于LiteNES的超级玛丽(具体操作请参考该目录下的README.md
).
事实上, 我们在PA进行到中期时也需要进行图像的输出, 因此你务必完成X Server的配置.
你被计算机强大的能力征服了, 你不禁思考, 这到底是怎么做到的? 你学习完程序设计基础课程, 但仍然找不到你想要的答案. 但你可以肯定的是, 红白机模拟器只是一个普通的程序, 因为你还是需要像运行Hello World程序那样运行它. 但同时你又觉得, 红白机模拟器又不像一个普通的程序, 它究竟是怎么模拟出一个红白机的世界, 让红白机游戏在这个世界中运行的呢?
事实上, NEMU就是在做类似的事情! 它模拟了一个x86(准确地说, n86, 是x86的一个子集)的世界, 你可以在这个x86世界中执行程序. 换句话说, 你将要在PA中编写一个用来执行其它程序的程序! 为了更好地理解NEMU的功能, 下面将
- 在GNU/Linux中运行Hello World程序
- 在GNU/Linux中通过红白机模拟器玩超级玛丽
- 在GNU/Linux中通过NEMU运行Hello World程序
这三种情况进行比较.
+--------------------------------+
| "Hello World" program |
+--------------------------------+
| GNU/Linux |
+--------------------------------+
| Computer hardware |
+--------------------------------+
上图展示了"在GNU/Linux中运行Hello World程序"的情况. GNU/Linux操作系统直接运行在计算机硬件上, 对计算机底层硬件进行了抽象, 同时向上层的用户程序提供接口和服务. Hello World程序输出信息的时候, 需要用到操作系统提供的接口, 因此Hello World程序并不是直接运行在计算机硬件上, 而是运行在操作系统(在这里是GNU/Linux)上.
+--------------------------------+
| Super Mario |
+--------------------------------+
| Simulated NES hardware |
+--------------------------------+
| NES Emulator |
+--------------------------------+
| GNU/Linux |
+--------------------------------+
| Computer hardware |
+--------------------------------+
上图展示了"在GNU/Linux中通过红白机模拟器玩超级玛丽"的情况. 在GNU/Linux看来, 运行在其上的红白机模拟器NES Emulator和上面提到的Hello World程序一样, 都只不过是一个用户程序而已. 神奇的是, 红白机模拟器的功能是负责模拟出一套完整的红白机硬件, 让超级玛丽可以在其上运行. 事实上, 对于超级玛丽来说, 它并不能区分自己是运行在真实的红白机硬件之上, 还是运行在模拟出来的红白机硬件之上, 这正是"虚拟化"的魔术.
+--------------------------------+
| "Hello World" program |
+--------------------------------+
| Simulated x86 hardware |
+--------------------------------+
| NEMU |
+--------------------------------+
| GNU/Linux |
+--------------------------------+
| Computer hardware |
+--------------------------------+
上图展示了"在GNU/Linux中通过NEMU执行Hello World程序"的情况. 在GNU/Linux看来, 运行在其上的NEMU和上面提到的Hello World程序一样, 都只不过是一个用户程序而已. 但NEMU的功能是负责模拟出一套x86硬件, 让程序可以在其上运行. 事实上, 上图只是给出了对NEMU的一个基本理解, 很多细节会在后续PA中逐渐补充. 为了方便叙述, 我们将在NEMU中运行的程序称为"客户程序".
上述描述对你来说也许还有些晦涩难懂, 让我们来看一个ATM机的例子.
ATM机是一个物理上存在的机器, 它的功能需要由物理电路和机械模块来支撑. 例如我们在ATM机上进行存款操作的时候, ATM机都会吭哧吭哧地响, 让我们相信确实是一台真实的机器. 另一方面, 现在第三方支付平台也非常流行, 例如支付宝. 事实上, 我们可以把支付宝APP看成一个虚拟的ATM机, 在这个虚拟的ATM机里面, 真实ATM机具备的所有功能, 包括存款, 取款, 查询余额, 转账等等, 都通过支付宝APP这个程序来实现.
同样地, NEMU就是一个虚拟出来的计算机系统, 物理计算机中的基本功能, 在NEMU中都是通过程序来实现的. 要虚拟出一个计算机系统并没有你想象中的那么困难. 我们可以把计算机看成由若干个硬件部件组成, 这些部件之间相互协助, 完成"运行程序"这件事情. 在NEMU中, 每一个硬件部件都由一个程序相关的数据对象来模拟, 例如变量, 数组, 结构体等; 而对这些部件的操作则通过对相应数据对象的操作来模拟. 例如NEMU中使用数组来模拟内存, 那么对这个数组进行读写则相当于对内存进行读写.
我们可以把实现NEMU的过程看成是开发一个支付宝APP. 不同的是, 支付宝具备的是真实ATM机的功能, 是用来交易的; 而NEMU具备的是物理计算机系统的功能, 是用来执行程序的. 因此我们说, NEMU是一个用来执行其它程序的程序.
你或许还对虚拟机和模拟器这两个相似的概念感到疑惑, 毕竟它们都表示用程序的功能来实现某些东西. 虚拟机就是用程序虚拟出来的机器; 而模拟器的范围则更加广泛, 可以用程序来模拟天体运动, 大气环流, 分子碰撞等等, 然而这些模拟的对象并不是一个计算机系统. 当我们用模拟器来模拟一个计算机系统的时候, 它和虚拟机在本质上并没有太大的差异. 所以我们说NEMU是个x86模拟器, 或者说NEMU是个x86的虚拟机, 其实可以认为是同一个意思: NEMU是用程序来实现一个计算机系统的功能, 并不是一个物理上的计算机.
假设你在Windows中使用Docker安装了一个GNU/Linux container, 然后在container中完成PA, 通过NEMU运行Hello World程序. 在这样的情况下, 尝试画出相应的层次图.
嗯, 事实上在Windows中运行Docker container的真实情况有点复杂, 有兴趣的同学可以参考虚拟机和container的区别.
NEMU的威力会让你感到吃惊! 它不仅仅能运行Hello World这样的小程序, 在PA的后期, 你将会在NEMU中运行仙剑奇侠传(很酷! %>_<%). 完成PA之后, 你在程序设计课上对程序的认识会被彻底颠覆, 你会觉得计算机不再是一个神秘的黑盒, 甚至你会发现创造一个属于自己的计算机不再是遥不可及!
让我们来开始这段激动人心的旅程吧! 但请不要忘记:
- 机器永远是对的
- 未测试代码永远是错的
- RTFM