为什么汇编语言的功能在高级语言中一部分成为了关键字,一部分封装成了函数?

为什么汇编语言的功能在高级语言中一部分成为了关键字,一部分封装成了函数?

invalid s,我特别喜欢和聪明人交往,因为不用考虑他们的尊严——乔布斯

这个问题下的回答啊……


简单说:设计高级语言的人才不理你汇编呢。

他关注的是他自己的抽象,至于硬件是 X86/X86_64 还是 ARM/RISC,关我屁事。

就好像设计 CPU 的只管我的 CPU 图灵完备、同时执行目标任务(标量、浮点)时性能达标——你管我把这玩意儿设计成复杂指令集、精简指令集还是超标量或者超长指令字呢。

这就叫“一层有一层的抽象”——只要我的抽象能用、好用,随便你上层下层怎么折腾。


举例来说,C 语言的抽象是什么?

抽象的是:一颗支持顺序、分支(if/else/switch)、循环(for、while)的 cpu,和一片平坦连续的内存,所组成的图灵机。

类似的,Java 抽象的是什么?

一颗提供类似 C 的支持、但禁止裸内存访问的图灵机。

这个图灵机除了 C 所支持(但不包括内存访问)的东西外,还提供了面向对象(class)以及内存自动回收等支持。

再比如,Haskell 抽象的是什么?

抽象的是一台支持 lambda 演算的机器——注意,虽然已经证明了 lambda 演算和图灵机等价,但它却不是图灵机哦。

这就是它们的核心抽象。


总之,一切高级语言,它都是类似“读写头 + 纸带”的、最简图灵机抽象的类似物。它压根不需要考虑某种特殊的 CPU 搞出了什么支持、更不关心能不能充分利用它——只要核心抽象有了,剩下都是编译器的工作。

比如说,现代 CPU 都有 SSE/AVX 指令支持,这些 SIMD 指令怎么办?

答案是凉拌。没有哪种语言从一开始的抽象就加入这些玩意儿。

但这东西真的能提速啊?

简单。

谁想用这种加速,自己想办法——或者内嵌汇编,或者寻找别人搞出来的现成的库。比如 openMP。

甚至于,现代语言很容易扩充语法。这段程序能并行?加一行@开头的指示就行了。

总之,除非真的通盘考虑过,否则不要瞎支持什么底层细节,更不要直接在高级语言抽象里把具体的 CPU 扯进来。

举例来说,你可以支持“并行编程”,但却绝不能支持 SSE/MMX。因为这些玩意儿其它 CPU 上面可能没有、或者虽然有但却是另外一套不兼容的体系。

因此,你要把这些东西笼而统之的叫做“并行编程”,然后自顾自的设计自己的并行编程语法——把你的这套语法翻译成具体的指令是编译器的事,语言设计层面不要管它。


类似的,回到问题:

这些东西,没有任何编程语言会直接支持。它们只会以官方库或者第三方库的形式支持。

这是因为,无论在图灵机抽象、x86 抽象、arm 抽象或者别的什么抽象里,它们都不是计算机的一部分。

那它们是什么?

它们是外设。

什么是外设?

外设是通过总线、串口、usb、网口、dvi、hdmi、dp 等等通讯接口、和运行程序的那颗主 CPU 通讯的、受主 CPU 控制的下位机。

没错。下位机。另一台计算机。

鼠标里面有一颗 386 甚至 486 级别以上的 CPU;键盘里面也有一颗比当年的红白机更强大的 CPU;蓝牙芯片,对,就你蓝牙耳机里面那个大米粒大小的芯片,那也是一颗主频 66MHZ 以上的强大 CPU。

硬盘主控芯片是一颗 CPU,DMA 控制器也是一颗 CPU;声卡里面有 CPU,网卡里面也有 CPU——显卡?显卡更不用说了……

所以,无论你要外设做什么、或者想从外设取得什么,你都要和这些 CPU 通讯。

当然,其中键盘、鼠标、显示器几乎每台机器都有,所以和它的通讯是标准化的。比如文本模式下,你从标准输入(stdin)读取、或者写入标准输出(stdout)即可——但根本上,x86 平台上,你是通过 out/in 指令,通过外设专用总线直接对它下发了指令字。

事实上,很多外设都有自己的标准化的控制协议。比如打印机可以通过并口、USB 口或者蓝牙通讯,和它接上头后,你可以用一套 AT 指令控制它的一举一动。

同样的,由于打印机用的太多太方便了,所以很多程序库内置了打印机支持——把你想打印的图片 / 文本丢给它就行了,你不需要懂 AT 指令。

换句话说,抽象这些外设的任务就交给了各种各样的库——这种东西当然不应该放到语言抽象里,对吧。

总之,一旦理解了这些东西、又明白了外设控制 / 通讯常用的手法(比如轮询和中断有什么区别、DMA 是什么等等)以及外设的工作原理(以及大致的控制手法 / 思路),那么再看相应的库,自然掌上观纹一样清晰了。