我花了几年时间用C和Java编写代码。我喜欢如何让一个程序显示消息框或执行一些文件操作,并且它可以在每一个(我认为?)CPU上工作而没有任何戏剧性。尽管我从未编写过任何高级代码。
我想学习汇编,只在Windows上。我认为我应该学习一个叫x86 asm的东西。
**我的问题:**如果我不想做一些太疯狂或晦涩难懂的事情,我创建的程序能在每个CPU上工作吗?我的重点是你家里的普通计算机或服务器。
很多人告诉我,我必须从许多有信誉的来源中为特定的CPU选择特定的架构,但我随后看到了在几个不同CPU上工作的代码示例...相互冲突的信息!
**编辑:**我只是想写一些程序,在汇编中找点乐子,而不必担心我的朋友John Doe在他的电脑上使用它会有麻烦。我可以很容易地用C编写一个程序,它会显示一条消息,pinggoogle.com和其他任何东西,而不必担心CPU。asm真的不一样吗?o.O
**编辑2:**也许我越来越困惑,因为我不知道哪些CPU上的正常(没有花哨)程序编码的Windows在C工作...只有英特尔和AMD?
5条答案
按热度按时间k0pti3hp1#
IBM PC历史上围绕Intel处理器发展,从8086开始,然后经过80186、80286(仍然只有16位),80386(32位扩展),80486,奔腾(有时称为80586)、Pentium II(686 有时用于此CPU家族),然后后来的命名/编号不再那么简单。
从这些“x86”平台名称被创建,因为所有早期型号都以“86”结尾,并且86前面的数字被修改。
“x86”系列的CPU在新型号的设计方式上是非常特殊的--几乎完全向后兼容。因此80186和80286 CPU可以运行8086机器码,它们使用了所有原始的8086指令,并在此基础上引入了新的扩展。
第一个相当不同的CPU是80386,它引入了新的32位模式与32位寄存器和保护模式(286“保护模式”大多被忽略,只有16位)。仍然实现了向后兼容,因为80386在通电后将以16位模式启动,作为更快的80286工作,只需要更少的额外指令,然后代码可以根据需要将其切换到32位模式(通常现代OS引导加载程序会在OS加载过程的很早阶段进行切换)。
然后,80486和下一代Intel CPU再次向后兼容80386,只是扩展了原来的指令集(从80486 DX CPU和奔腾开始,每个x86 CPU现在都默认内置了浮点单元,尽管现代的x86已经过时了...对于旧的80386和80486 SX CPU,必须为HW FPU购买单独的x87协处理器芯片)。
与此同时,英特尔试图通过引入全新的64位平台(“安腾”或“IA-64”)来摆脱向后兼容性监狱(这使得现代x86 CPU的设计非常复杂和繁琐),这是不向后兼容的。市场确实对采用犹豫不决,这并不奇怪,因为所有旧的“x86”软件都不能在它上面工作。
不久之后,AMD推出了自己的64位扩展,这一次是围绕“x86”传统设计的,与386扩展286的方式类似,从客户的Angular 来看,它确实工作得更好(尽管这意味着汇编程序员要稍微棘手一点,例如
mov eax,1
将修改rax
寄存器的全部64位,自动将前32位归零,等等......这些古怪的“规则”中的一些使得原始32位指令集的64位扩展变得可行,并且从长远来看,它工作得相当好,即使在第一次读取时可能感觉有点像黑客)。所以现代的“x86”PC确实在单芯片中包含了三种主要的不同cpu,过时的16位80286,非常古老的32位“686”和当前的64位“x86-64”变体。从汇编的Angular 来看,它们都共享基本的指令和语法,所以如果你充分学习了“686”指令集,x86-64源代码看起来对你来说很熟悉。2只要你只使用指令集的基本子集(比如“686”子集),你的二进制代码就能在所有15- 20年前的x86个人电脑上的特定操作系统上工作。
同时AMD(以及Cyrix和其他试图在“x86”世界与Intel竞争的厂商)正在生产与Intel二进制兼容的CPU,有时他们试图引入一些扩展,如AMD的“3Dnow”,但它们只被程序员很少使用,通常会被放弃,唯一的例外是目前的64位模式,这是由AMD和英特尔设计的,最终不得不给予,并将其从AMD复制到他们的CPU中(因为他们的安腾IA-64没有被市场接受,由于缺少向后兼容性)。
但是,如果您像游戏开发人员一样,追求最高性能,则必须在运行时检查CPU型号/功能,并根据指令扩展的可用性,为特定CPU提供不同的二进制变体,例如最多使用SSE 2指令的变体,能够在所有64位CPU上运行,以及使用SSE 4和AVX 512等新扩展的其他变体。这将仅对具有最新CPU的消费者的小群体起作用。
许多x86上的C编译器默认目标是32或64位模式(主要取决于操作系统或您的项目设置),并且只使用非常有限的指令集,例如大多数只有“686”,或者x86_64甚至没有SSE 1/2,因此它们的二进制文件可以在任何常见的x86 PC上工作。
这也是你可以学习的第一件事,如果你的目标是“x86”汇编知识,从基本的指令集开始,比如可能只有80386,学习它的原理,然后看看它是如何扩展的(跳过16位80286,那只是无用的折磨,32位模式要简单得多,那么如果你真的好奇,你可以试着看看16位有什么不同,32位的体验会让它更容易一些,但这是毫无意义的)。
64位模式比32位模式稍微复杂一点,但也没那么糟糕,如果你愿意的话,你甚至可以从64位模式开始(没有比16位模式更复杂的地方)。
顺便说一句,你仍然应该从C-lib开始I/O和Win API调用,也就是说,你需要C编译器。(如果您只是在学习ASM基础知识,那么这只是不必要的干扰),而且您无法在现代操作系统下直接访问HW,因此没有OS API服务就无法执行I/O,这与旧的16位模式不同,在没有保护的环境中,你可以自由地访问硬件外设。你可以从C Package 器调用你的asm函数,然后它可以处理I/O、内存管理和其他与OS API相关的事情,专注于你的asm函数中的纯算法和asm编程(这也是汇编在真实的项目中的使用方式,现在您不用ASM编写整个应用程序,而是用C/C编写,只在ASM中重写性能最关键的部分,在你有了工作的C/C原型,并且你通过分析确定了特定的瓶颈之后..没有必要在汇编中编写其他部分,比如文件阅读等,太麻烦了,没有任何好处)。
顺便说一句,C语言是可移植的语言。如果你只使用标准的C库,它将工作(源)对几乎任何东西,但只有在你将编译它为特定的目标平台二进制。
windows的世界很小,大多局限于x86(IA-64和DEC Alpha有Windows变体,它们不能运行x86二进制文件,但它们是从事专门工作的专业机器,公众不太了解)。因此,如果您使用默认选项编译Win32 x86可执行文件,in将运行(次优)在99%的windows计算机上,仅使用有限的x86指令子集。如果您将打开编译器以使用CPU的一些现代功能,则生成的二进制文件可能无法在John Doe的PC上工作,如果他的x86 CPU不支持你的机器代码使用的功能。
对于许多应用程序来说,这个默认子集已经足够了,您不需要再为扩展指令而烦恼。只有少数应用程序需要最高性能,如游戏、CFD或其他科学计算......您的普通Web浏览器运行时的+5-10%性能损失可以安全地忽略(通常在driver/etc中的某个地方会有一些针对当前CPU的微调代码,它确实可以处理对性能要求较高的主要任务,如解码视频/etc,因此即使是用通用“x86”目标编译Web浏览器也将从中受益)。
你不需要产生9999个asm代码的变体,如果你只想让它运行,基本上你只需要32或64位变体(取决于目标,我不知道windows世界,但与现代操作系统,你可以安全地只针对64位,这就像90+%的用户),使用基本的x86指令,这将工作“无处不在”(在x86窗口)。
但是这样使用asm没有多大意义(除了教育目的,完全从这里开始是非常有意义的),因为这样的asm的性能将是次优的,所以你已经可以使用C或C来获得类似的结果。为了有意义的使用,你还必须学习现代扩展,做可用特性的运行时检查,并动态加载你的函数的正确变体,为特定的CPU类型使用最佳的机器码。这就是你可能要为同一个函数写9999个变量的地方(这并不坏,通常4-7个变体可能会覆盖大多数可用的CPU,一个兼容性回退仅使用基本指令集,然后是一些更专业的指令集,如SSE 3、+SSE 4、AVX 1/2、AVX 512、另外,真实的世界中需要最高性能的软件非常罕见,即使是大多数简单的游戏也完全可以使用次优的二进制文件,只有当你在像虚幻引擎开发人员这样的尖端工作时,你才必须关心这些微调。大多数时候,收益是不值得投资的。
毕竟,现在有这么多的软件是用C#、Java甚至JavaScript(或者PHP......我真的提到过吗?现在感觉很脏)编写的,显然性能不是问题,否则大部分软件都会在一段时间后被重写为C,以提高性能。
cgvd09ve2#
你一直声称用C编写的程序可以被其他人在他们的计算机上使用而没有问题。只有当他们在SPARC工作站上运行Windows而不是Linux或OSX或OpenBSD或Solaris时,这才是真的...
在其他操作系统上显示窗口或执行网络I/O(“ping www.example.com“)所使用的C库google.com是不同的,尤其是
ping
是一个非常不可移植的东西,因为发送ICMP回应请求包通常是一个特权操作。(例如,Linux上的ping
可执行文件是setuid-root,因此它可以强制执行速率限制。)您可能会使用system("ping google.com")
而不是在自己的程序中使用网络套接字。一个更好的例子是发出HTTP请求,因为任何进程都可以打开TCP连接。你可以编写可移植的C程序,但你必须努力使它们具有可移植性。人们需要源代码,这样他们才能为自己的系统编译它。(这就是为什么Unix有以源代码形式发布软件的传统:每个人都需要用自己的库版本为自己的系统编译它。)
编译的二进制文件只能在目标平台上运行,例如x86-64 Windows。这样的二进制文件不能在ARM Windows或32位x86 Windows上运行。
在asm中编写代码时,您的 * 源代码 * 是特定于目标平台的(包括CPU,而不仅仅是可用的系统调用和库),因此您将为32位x86 Windows编写代码,而不仅仅是“for Windows”,C编译器可以从同一源代码生成x86 32位或x86-64 64位二进制代码。
如果你首先关心的是32位x86 Windows,这不会有太大的区别(每台“普通”Windows电脑都可以运行32位x86二进制文件,所以如果你想移植到其他Windows电脑上,这就是你应该做的)。
例如,让我们使用Godbolt编译器资源管理器(source+asm)来查看一个普通的C程序如何针对两个不同的x86平台编译为不同的asm:
编译为x86-64 Linux的asm(x86-64 System V调用约定),使用
gcc -O3
(Intel语法模式而不是AT&T,Godbolt将其传递为-masm=intel
):但是32位x86 Windows的MSVC将其编译为以下asm:
汇编语法是不同的(MASM vs. GAS),但这不是重要的,* 重要 * 的是指向字符串常量的指针是在堆栈上传递的(使用
push
),而不是在寄存器中传递的。在Windows上,
puts
的asm符号名称前缀为_
,但在Linux上不是。而且由于这是32位代码,堆栈槽的宽度只有4个字节,而不是8个。
这是针对ISO C函数,如
puts
,在两个平台上都可用。在Windows上,你可以在WinAPI DLL中调用MessageBoxA
,但Linux没有“本机”图形API。大多数桌面都需要X11库,但不是每个人都使用X11。既然你已经了解C语言,那么查看编译器输出是开始学习asm的好方法。参见Matt Godbolt的CppCon 2017 talk “What Has My Compiler Done for Me Lately? Unbolting the Compiler's Lid”。(关于编写编译成有趣asm的微型函数的更多建议:How to remove "noise" from GCC/clang assembly output?)。
7y4bm7vi3#
Assembler code不能在CPU上工作,需要通过一个叫做汇编程序的程序转换成machine code,这个转换通常很简单(通常,汇编程序把包含汇编代码的文本转换成一些目标代码;然后链接器将几个目标文件聚集成一个可执行文件,并解决重定位问题;该可执行文件包含机器代码)。
x86有几种汇编语法(例如nasm与gas)。
x86有几个变种(例如,一些处理器,但不是所有的,接受像AVX这样的扩展,有32位和64位)。
最后,Windows的用户级x86程序是一些executable,它不能在相同的x86体系结构上工作(甚至是相同的硬件)运行其他操作系统(例如Linux),因为ABI、system calls、可执行文件格式(Windows上的PE,Linux上的ELF)是不同的。阅读Operating Systems: Three Easy Pieces了解有关操作系统的更多信息。
wfveoks04#
让我们一步一步来:
14ifxucb5#
汇编语言有许多部分可以在每个CPU上工作,但对于不同的CPU,有些东西是不同的,因为汇编语言直接与CPU通信(不像高级编程语言)。要理解为什么,必须了解CPU。
CPU具有用于处理的临时存储器。其中一些存储器用于特定目的,但大多数存储器用于任何用途。不同的CPU允许在这些容器中存储不同数量的数据。此外,一些CPU的存储容器比其他CPU少。x86 CPU没有x64 CPU那样多的存储容器或处理能力。
在x64 CPU上,可以直接将1084848和10487583848相加(这些数字完全是随机的),但对于x86,可能必须将该问题分解为多个部分。
在x64 CPU上,.long可以存储8个字节(64位),但在x86 CPU上,.long可能无法存储8个字节。
而且这些都有例外,因为并不是所有的x86系统都是一样的,就像上面回答的那样,有些x86使用16位,有些16位和32位都使用。
您可能想了解更多信息,请访问:https://archive.org/details/ost-computer-science-programminggroundup-0-9/page/n26/mode/1up?view=theater和地址:https://nixhacker.com/getting-processor-info-using-cpuid/amp/
只要它能在我的系统上运行,我就不会太担心它,因为从技术上讲,没有一个系统的编译方式是相同的,而且许多功能在其他系统上也不起作用。如上所述,你的C程序不能在每个系统上运行。这就是为什么微软会制作多个版本,并根据它们所适用的操作系统来标记它们。
如果你的程序能处理所有的问题,也许你不应该太担心,除非你想制作多个版本。