计科幼幼班

今天的分享可以(粗浅)解答以下几个问题:

  • 如何制作一台计算机
  • 苹果的 Rosetta 是怎么把 x86 的程序运行在 arm 上的
  • Steam Deck 是怎么把 Windows 的游戏运行在 Linux 上的

如何制作一台计算机

逻辑门是什么?

是数字逻辑电路中的基本单元,可以用真值表定义一个逻辑门,以 NAND(Not AND)逻辑门为例:

Input A Input B Output
0 0 1
0 1 1
1 0 1
1 1 0

Q:它有什么用?
A:我们只用它就可以制作出计算机

开工:https://nandgame.com

先来制作 NAND 本身,下面是用于制作 NAND 的原材料:

三个圆是元器件的引脚,上面的引脚是输出,下面是控制(c)和输入(in)。

控制端的原理很简单,就是电磁铁,c 通电,则 relay 的舌片被磁铁吸住,控制 relay 开或关。

in 是电源,始终有电,我们把有电的状态叫做 1,没电叫做 0

relay(default on):

  • c 没电,则电路接通,输出 1
  • c 有电,则电路断开,输出 0

relay(default off):

  • c 没电,则电路断开,输出 0
  • c 有电,则电路接通,输出 1

实践时间,不提供解法。跟着 nandgame 一路把第一部份所有逻辑门写完。

到这里对数字电路应该有了初步的认识,但这个课程后面还有很多东西,有条件的话我们后面再分享:

  • ALU(算术逻辑单元)
  • 如何实现存储(bit, byte, 寄存器,内存)
  • 机器语言与汇编语言,汇编器
  • 高级编程语言,编译器
  • 操作系统
  • 应用程序

苹果的 Rosetta 是怎么把 x86 的程序运行在 arm 上的

https://support.apple.com/zh-tw/HT211861

Rosetta 2 可讓配備 Apple 晶片的 Mac 使用專為配備 Intel 處理器的 Mac 所開發的 App。

比如我们制作了一个 CPU,它支持一系列指令(只是举例哈):

  • 写入寄存器a
  • 读取寄存器a
  • 写入寄存器b
  • 读取寄存器b
  • 将第一个、第二个数相加,结果写入寄存器a

指令集就是规定了这样一系列指令,如果某个 CPU 实现了这些指令,就可以说它支持某个指令集。

然后我们写了一段代码1x3+2=?,并以这个 CPU 为目标,编译出来的二进制形如:

  • 将 1 写入寄存器a
  • 将 1 写入寄存器b
  • 相加
  • 将 1 写入寄存器b
  • 相加
  • 将 2 写入寄存器b
  • 相加
  • 读取寄存器a

这个二进制在我们的 CPU 上执行完,寄存器a中的数字就是代码的执行结果。但是如果我们拿为另一个 CPU 编译的二进制,在我们的 CPU 上执行:

  • 将 1 写入寄存器a
  • 将 3 写入寄存器b
  • 相乘 <<<<
  • 将 3 写入寄存器b
  • 相加
  • 读取寄存器a

执行就会失败,原因是我们的 CPU 并不支持 相乘 这个指令。简单来说,X86 和 ARM 就是这样两个互不相容的指令集,所以为 X86 编译的二进制不能运行在 ARM 上。

那 Rosseta 是怎么做到的呢?还是以上面的相乘操作举例,我们可以把 1x3 改成 1+1+1,将不支持的 x 改成 + 来实现,这样就能在我们的 CPU 中运行为其他 CPU 编译的二进制了。

Steam Deck 是怎么把 Windows 的游戏运行在 Linux 上的

Steam Deck 的 CPU 是 X86 的,Windows 上的游戏(大多)也是为 X86 编译的。既然它们的 CPU 都是相同的指令集,为什么二进制不能共用呢?

因为 Steam Deck 运行的 Steam OS 是 Linux 的,所以问题其实是,应用程序为什么不能跑在不同的操作系统上。

这里牵扯到两个概念:

首先可执行文件格式是啥?exe?但 exe 在 linux 中是运行不了的。

举个可能不太恰当的例子,我们编写了一个图片查看器,支持 jpg 格式。这时如果要求它打开一个 png 图片,它就会说“未知文件格式,无法打开”。

是我们的屏幕无法显示 png 中保存的图片么?不是的,是我们的图片查看器不知道如何提取出 png 中的图片信息,它不认识 png 这种图片格式。

在这个例子中:

  • 图片查看器 -> Window
  • jpg -> exe(PE)
  • png -> ELF

Window 认识 exe(PE) 这种可执行文件格式,可以解读并且让 CPU 执行其中的指令。

但不认识 ELF 这种可执行文件格式,所以无法解读出其中的指令。所以不是 CPU 不能执行指令,而是操作系统不知道怎么提取出指令,进而交给 CPU 执行。

PE 可执行文件格式:

所以 wine 是怎么在 linux 中执行 exe 的呢?既然操作系统不认识 exe,wine 自己去解析就行了嘛。然后把解析出的结果交给 linux 去执行就可以了。

但实际上还是不行,原因是不同的操作系统提供的“操作系统 API”不一样。比如 Windows API 中包含 GUI 相关的 API,创建窗口之类的,但 POSIX 中没有。

假设有这样一个 OS A,他的窗口必须“有,且只有一个标题”,当二进制想要创建一个自己的窗口,可以调用这个 OS 提供的一个 API:

// 创建一个标题为 Hello world 的窗口
create_window("Hello world")

但还有另一个 OS B,它规定窗口必须有主标题和副标题,它提供的创建窗口 API 可能就是这样:

// 创建一个标题为 Hello World,副标题为 subtitle 的窗口
create_window("Hello world", "subtitle")

如果我们尝试在 OS B 上执行为 OS A 编写的应用程序,当调用到 create_window 这个操作系统 API 时,就会出错,因为少传了副标题。

所以 wine 还得为 window 的应用程序提供兼容的操作系统 API,当应用程序调用到某个 Windows API 的时候,转为帮它调用对应的 linux 下的 API。

这样一来,对应用程序来说就像是在 Windows 中运行一样。

注意:文中的例子都是超级简化版本,只为了方便理解不保证正确全面,不要当真

posted @ 2023-09-07 12:34  ALANALAN  阅读(9)  评论(0编辑  收藏  举报