不懂就问,为啥同一个软件不能用在 x86 和 amd 的 CPU 上?

2020-06-26 21:00:28 +08:00
 x97bgt

我的理解是,这两种架构的指令集不一样,但只要操作系统做好封装,那么软件就不用去关心用的是哪种 CPU 架构。用何种架构只要操作系统感知到就可以了。

难道软件需会直接去执行汇编语言吗?

求解惑。

4178 次点击
所在节点    程序员
28 条回复
nightwitch
2020-06-27 10:30:21 +08:00
"但只要操作系统做好封装,那么软件就不用去关心用的是哪种 CPU 架构"

确实存在,果子和微软都做过类似的。操作系统接手指令翻译,把 x86 的指令就翻译成 arm 的指令,这样软件是不需要关心 CPU 架构的,由操作系统来对应翻译到目标平台,就像托管型语言不需要关心目标平台,由虚拟机来翻译成 native 语言。 当然,现实是骨感的,绝大多数这种翻译都会拖慢性能,并且带来兼容性问题。
x97bgt
2020-06-27 12:19:32 +08:00
@SingeeKing @chiu @delectate @qakito @nightwitch @libook
感谢大佬回答,尝试写一下我的总结。

软件最后都是可执行文件,里面都是机器码,这个对于不同 CPU 架构是平台各异的。所以对不同的 CPU 架构,软件要有不同的发行版,毕竟机器码都不同了。

我对 arm 和 x86 的 CPU 架构的指令集不熟悉,但我想两家能做东西差不多,一家能做的另一家也应该有对应的替代指令。如果有一个基础库将两种架构都整合得很好,那使用这个基础库的软件,应该一份代码就可以编译出不同平台的发行版。

但如果软件需要深度地直接操作 CPU 的指令,那么就针对不同平台就需要有一份不同代码。

不知道理解得对不对,轻拍~
msg7086
2020-06-27 13:22:50 +08:00
> 一家能做的另一家也应该有对应的替代指令
没错,大家都是 CPU,运算当然都能做。

> 如果有一个基础库将两种架构都整合得很好
编译器本身就能把 C 或者高级语言代码编译到目标平台,一般不需要基础库。

> 但如果软件需要深度地直接操作 CPU 的指令
是的,问题就在这里。
比如说很多软件是想当然地「默认」自己会运行在 x86 平台上,所以很多假设都是基于 x86 的。
(甚至有很多软件都是默认自己运行在 Windows 或 Linux 上,导致移植到另一个平台时需要大改代码。)

比如说我现在用的一个软件,2002 年写的,源代码里到处充斥着 MSVC 格式的内联 MMX 汇编。
这意味着什么呢?
首先 MSVC 格式的内联汇编只被 VC 支持,而且只能运行在 32 位上。
所以 Linux 就不能用了,GCC 也没法编译。
其次内联汇编和 MMX 只能运行在 32 位上,所以没办法编译到 64 位。
而且很显然,x86 汇编不能运行在 ARM 上。

那么怎么办呢?

很简单 —— 重 写。

之所以会出现这个问题,就是因为代码编写的时候,一来没有这个技术,二来没有想到以后要移植,所以用了当时的技术和方法去写的代码。将近 20 年过去了,C++也从 1989 版变成了 2017 版,CPU 指令集也从奔腾 3 时代的 ISSE 变成了现在的主流 AVX2,Linux 服务器也开始进入普通人的生活,很多以前都没想过的技术,现在都已经变成了理所当然。但是源代码不会自己进化,还是需要时间精力的投入。这就是为什么小众平台软件支持更差的原因。

另外,就算是能在 ARM 上编译运行,也会因为缺少 SIMD 优化而变得非常缓慢。现代处理器重度依赖 SIMD 并行运算指令,而 ARM 上用的则是 Neon,不仅机器指令不同,上层的 Intrinsics 设计得也完全不一样。没有了 SIMD 优化,运行速度会直接下降一个数量级。所以就算能用,也不一定用得舒服。
gosas
2020-06-27 13:52:25 +08:00
您是指 cisc 和 risc 的区别吗?
wanguorui123
2020-06-27 17:48:24 +08:00
x86 的是复杂指令集,程序发起一条指令可能完成许多个动作,比如:把大象放到冰箱里,一条指令可以完成,对程序来说不用关心具体细节,这一点就像数据库中存储过程一样,调用时候不用关心具体细节,这样可以提高特定任务的执行效率,但是指令集会越来越庞大,以及难以维护
ARM 的是精简指令集,程序发起一条指令只能完成一个具体动作,同样一个任务可能需要编写更多指令去完成,理论上在执行某些特定任务的效率低于 x85 平台,但是可以通过优化 CPU 的指令的执行吞吐量,以及集成专用处理器单元来弥补缺陷
理论上 x86 的复杂指令可以提前转换为多条 ARM 的精简指令来执行,这就是 MacOS 11 中 Rosetta2 的作用


但是要想彻底发挥出处理器的性能,可以将源代码编译成特定平台的程序,这样就省略运行时转换为特定平台的指令这个步骤,同时在源代码层面编译优化应该可以比 Rosetta2 这种在指令上兼容的模式更加优化和高效,C/C++的代码一般用这种模式

另外像 JAVA/C#这种语言就有点不同了,先将源代码打包编译成中间代码的程序包,这个中间代码的程序包发布到不同 CPU 平台,由 JVM/运行时环境根据平台再次将中间代码编译优化成特定平台能识别的程序指令
zjsxwc
2020-06-27 19:01:43 +08:00
js 啊,js 无所不能
jim9606
2020-06-27 20:09:48 +08:00
可能你对用户程序的理解有点问题。

通常用户程序里的二进制码是**直接在 CPU 硬件上执行**的,所以是 arch 相关的。这部分跟 OS 无关。

但是用户程序必然要用到一些 OS 提供的功能,例如访问网络(需要 OS 网络栈+网卡驱动负责具体实现)、访问文件系统(需要文件系统驱动+磁盘控制器驱动负责实现),需要通过 OS 及 OS 提供的一些用户库进行。这部分是有办法做到 arch 无关的。例如 windows 使用 WOW64 提供 ABI 兼容的 dll 供 32 位程序使用,这些 WOW64 dll 负责将 32 位 ABI 的调用翻译 OS 64 位 ABI 的 dll 上。

Java Bytecode 这种需要 runtime 的二进制码才是楼主所说的与 arch 无关,因为这种二进制码需要 arch 相关的 VM 将其转换为对应的 OS 系统调用和在 CPU 硬件上执行的二进制码。

事实上功能简单的程序是可以轻松跨平台的,但目前大部分复杂点的软件都没法做到。
classyk
2020-06-27 22:02:01 +08:00
Android 如果存用 java 开发,开发出来的软件,一套就可以在 android x86,也可以在 android arm 上运行。
如果涉及到 jni,那就要为不同的系统编译不同的 so 了

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/684925

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX