编译器、虚拟机、操作系统,到底哪个更难?
实际上,除了MATLAB这样的数学软件之外,肯定是编译器更难!
虚拟机和操作系统更多的是麻烦,工作量大,而不是难。
1、虚拟机
什么是虚拟机?
能够运行字节码的程序,就是虚拟机。
CPU的机器码就是一种字节码,它是直接在硬件上跑的,由硬件的数字电路来保证它的运行。
但是虚拟机是由软件程序来保证字节码的运行的。
软件程序是高级语言写的,可以写非常上层的逻辑,实现起来比数字电路简单得多。
到了字节码(机器码)这个层面,逻辑已经非常简单了,远不如高级语言的源代码复杂。
让字节码运行起来,实际上比编译器生成字节码更简单:
因为生成字节码是编码,而让字节码运行是解码,任何时候都是编码比解码更复杂。
编码,需要把杂乱的信息整理成有序的。
解码,只需要把有序的信息顺序读出来就行。
所以,H264编码的CPU消耗,远比H264解码更大!
如果字节码类似RISC架构的机器码(例如ARM),那么每4字节就是一条指令,指令里的每一位做什么都是固定的。
所以,虚拟机的代码就是这样的:
这种程序很难吗?
不难。
机器码的逻辑是特别简单的,比高级语言的代码简单得多!
尤其是RISC架构的,更是比x64的机器码还简单。
x64的机器码因为长度不固定,解释起来要一个字节一个字节的分析,稍微复杂一点,但复杂度也远不如高级语言的源代码!
qemu复杂,是因为它要模拟多个型号的CPU。
如果只是给字节码实现一个跨平台的虚拟机,并不难。
把java源代码变成字节码的过程,远比让java字节码运行起来,要难得多:
前者是编译器,后者是虚拟机。
2、操作系统
如果只是让OS内核在CPU上跑起来,大概只需要5000-8000行的C代码!
Linux 0.01版(即第一个Linux版本)的代码量也就在8000行左右。
Linux 0.11版,大约不到2万行。
与编译器比起来,操作系统只是更麻烦!
因为要支持的驱动模块很多、要支持的文件系统很多、要支持的网络协议很多,这些模块的代码都是工作量
但是,麻烦不等于难!
8000行代码的OS内核(例如Linux 0.01),只需要实现进程管理、内存管理、控制台管理、键盘驱动、硬盘驱动,另外支持一种简单的文件系统,就可以跑得起来。
这样的OS内核实际上已经很完善了
剩下的都是在文件系统的底下添加驱动模块、网络协议模块。
按照unix一切皆是文件的设计哲学,外设的驱动模块和TCP/IP协议,都是隶属于文件系统的子模块。
shell(命令解释器)不属于OS内核,而是一个用来解释命令的用户态程序。
当然,shell对系统的使用来说是必需的。
在文件系统的API基础上,实现列目录、创建目录、创建文件之类的功能并不难。
当然,这些命令实现起来的工作量,比让一个8000行的OS内核运行起来还大。
3、编译器
光一个语法分析就可能超过1万行!
如果语法像C++那么复杂,那语法分析的代码量更大。
(如果用第三方的正则表达式库的话,第三方库的代码也没有低于1万行的)
而且编译器的实现中有一些非常别扭的地方,例如C++的如下代码:
两个> >之间必须有一个空格,否则g++是会报错的。
之所以会这样是键盘上的符号太少了,而C++的语法太复杂,在编码上实在应付不过来了。
另外,编译器的后端也有一些非常复杂的模块,例如:指针分析、自动内存管理、循环分析、寄存器分配、goto的处理,等等。
还有一种是并行分析:
显然,N个源位置与N个目的位置是不可能相同的,所以它可以并行复制数据。
人一眼就可以看出来源位置与目的位置的数组读写是不相关的,但用代码怎么判断?
要用整数线性规划。
运筹学上有这一章,龙书(编译原理)里也有提到,我也曾经学过但都忘了
虚拟机和操作系统真只是工作量大,论难度还是编译器和MATLAB!
我的gitee上也有一个bochs上的内核demo,有兴趣的可以看看,实现起来比scf简单多了。
scf编译器的后端只加了必需的模块,都写了4万行代码。