现在嵌入式程序开发使用最多的编程语言无疑是C语言和C++,也许有些特殊的关键代码段需要使用汇编语言编写,不过根据巴尔集团(Barr Group)最近的一项研究,目前超过 95% 的嵌入式系统代码就是用C语言和C++编写的。
然而,世界上唯一不会变就是改变——不断有新的编程语言,新的体系结构诞生,不断挑战C语言在嵌入式领域的地位。根据巴尔集团最近的一项调查,现在发展最快的嵌入式程序开发语言已经不是C语言了,而是 Python。
相当多的C语言/C++程序员看到这样的调查结果,不禁提出质疑:Python 能够产生更快、更紧凑、更可靠的代码吗?这其实很像 20 年前的汇编代码专家对C语言在嵌入式领域的迅速发展提出的质疑。
不得不承认,新的编程语言涌入嵌入式领域是有可能导致更高的生产力的。
“改朝换代”?
在之前,很多学校开设的计算机编程语言课程一般都是C语言课程,完成课程需要完成一个C语言程序项目。但是现在不是了,国外已经有很多学校已经开始将Python作为入门编程语言了。计算机科学专业的应届毕业生可能在 Python、Ruby 和其他几种简单的脚本语言间有丰富的经验,但是却从未认真使用过C语言开发项目。
另外,现在很多嵌入式设备也开始使用 Android 作为连接用户交互的平台,这就为 Android 原生语言 Java 打开了大门。许多通过机器人、无人机或者其他类似的小型项目,接触嵌入式的业余爱好开发人员的经验,通常来自树莓派这样的高度紧凑,编写程序简单(一般都不使用C语言,而是类似于Python的简单语言)的环境。
比较热门的物联网(IOT)将 Web 开发人员也纳入了嵌入式系统开发中,因为物联网的系统外部交互也可以是一个静态的 Web 页面,这时 JavaScript 以及它服务端对应的 node.js 也将派上用场。C语言程序员应该注意 node.js 是一个可扩展平台,PayPal 以及 WalMaple 等大企业已经广泛使用,发展迅速。
物联网将嵌入式系统的任务分布到现实世界的客户端,通常对应着终端硬件设备,服务端则部署在互联网上。在这样的场景下,客户端看起来很像是由硬件支撑起来的 Web 应用程序。因此,对于 Web 程序员来说,物联网嵌入式系统很像是使用 JavaScript 和 node.js 开发的项目的一个子模块。
此外,嵌入式系统中的算法日益复杂,这是另一股促进变革的力量。随着简单的循环控制逻辑被卡尔曼滤波器、神经网络以及基于模型的控制逻辑取代,C语言已经有些力不从心,而开发方便的 Python 则显得游刃有余,其他一些基于模型的编程语言(如 matlab)也在这一领域有了立足点。
推进“改朝换代”的力量
现在嵌入式程序开发中,C语言仍然是毫无疑问的占据主导地位,为什么“新来的”程序员不愿意学习和使用C语言了呢?
“C语言程序开发起来太慢了。”这是很多程序员讨厌C语言的主要原因,他们一般还会认为使用C语言开发程序容易出错,会受到硬件依赖性影响,而且代码可读性不强(“某段C语言程序,除了最初的开发者,谁也看不懂”),他们认为,C语言的这些特性会妨碍程序员产生足够大的生产力。
相比之下,以 Python 为代表的新生语言则学习和使用起来都比较简单,虽然这些语言多多少少参考了C语言高度压缩的语法,但是代码的重点已经转向可读性了,而不是最小尺寸。另外,现在的一些编程语言还会受到结构约束,这使得代码可读性更强,甚至可以从结构化的注释中直接生成用户手册。
现代编程语言也包含更高层次的数据结构,当然了,C语言/C++ 也可以自定义各种复杂的数据结构,但是这些复杂的结构需要程序员记得住,分得清管理。相比之下,Python 支持原生的 array 和字典语法就方便多了。
一个颇具争议的问题是现代编程语言的动态类型。在使用变量时,解释器实际上是解释而不是编译代码,解释器会判断表达式的值的当前数据类型,并选择适当的操作,这样显然能够减轻程序员的工作。
不过嵌入式C语言程序员应该能够看出,所谓的“动态类型”也会同时降低程序的效率,另外解释器的判断是程序员不可控的,一旦判断数据类型出错,将导致程序出现莫名其妙的错误。
有人说 Python 编程实际上根本不是编程,而是写脚本——将其他人使用C语言编写的函数调用库串接在一起而已。
不利因素
前面提到以Python为代表的新生编程语言的好处,但是世界上不可能有完美的事情,Python 相比于C语言具备的优良特性,是以牺牲其他东西换来的。
与C语言相比,这些新生编程语言大都有一个很明显的问题:它们是解释型的语言,而不是编译型的语言。
这意味着,要运行一个解释型语言编写的程序,开销还会包括解释器本身的工作存储、动态类型的支持以及运行库等,这对于资源普遍比较匮乏的嵌入式系统来说,无疑是一个挑战。
有人将这些开销尽可能的降低到最小,但是不可避免的:一些 Java 虚拟机仍然需要数十兆字节的额外空间,服务器端的 node.js、python 等类似的解释型语言也常常需要数兆字节的额外空间。
然后就是性能问题。解释器读取每一行代码,不管读取的是源代码还是预处理过的中间代码,都需对其进行分析、执行运行时检查。原本C语言程序编译成两个机器指令就能解决的问题,现在却需要大量机器指令完成,这无疑会延长程序的执行时间和增大能耗方面的成本。
不过,性能问题可能并不是完全无法解决的障碍,改进它的一种方法是使用实时(JIT)编译器。顾名思义,JIT编译器与解释器并行工作,为循环内的代码生成编译后的机器指令,因此后续过程将更快地执行。
此外,程序调用的许多函数最初是用C语言编写的,这些函数可能以编译后的C语言程序速度运行,原因很简单,就是它们本来就是C语言程序。
值得说明的是,一些研究团队正在考虑在硬件加速器中实现嵌入式程序开发中可用的库或者模块,例如图像处理单元(GPU)、XeonPHI 或者 FPGA,在这样的场景下,解释型语言可能更加合适。
以 Python 为代表的解释型语言的另一个缺陷是缺乏处理真实世界的手段。最明显的一个例子是,Android环境将Java代码封装在一个几乎与硬件无关的抽象中:具有图形、触摸屏、音频和视频、多个网络和物理传感器的虚拟机。对于轻量级的嵌入式平台,可能更重要的需求是(甚至在微控制器MCU上)处理物理 I/O。
由于Python的解释器cpython运行在Linux上,因此原则上,Python程序可以在任何具有足够速度和物理内存的嵌入式Linux系统上运行。通过尽量减少Python解释器的开销,甚至可以在 STM32 微控制器上搭建裸机 Python 环境。同样的手段可以让 node.js 以及 javascript 引擎也能运行在微控制器上。
解释型语言应用到嵌入式领域还有个重要问题,即安全性问题。许多安全可靠性标准不鼓励或禁止使用未经正式验证或全面测试的开源代码。这样的限制可能会影响模块重用的便捷性,降低生产力。如果把目光扩展到虚拟机,也应该能发现像 cpython 这样的开源项目甚至会导致 python 程序从源头上不安全。
最后,就算嵌入式领域接受了这些新生编程语言的加入,不难想象多语言模块将带来的混乱,除非嵌入式系统能够支持所有编程语言程序运行,否则不同编程语言编写的库将无法通用。但是,资源匮乏的嵌入式设备支持一种解释型语言就已经很吃力,让其支持所有编程语言产生的开销对于嵌入式设备来说无疑是不可接受的。
最后
如今,绝大多数嵌入式代码都是用C语言编写的,C语言本身也的确非常适合嵌入式领域。但是,既然C语言能够逐步代替汇编语言,谁又能保证今后不会有其他编程语言逐步替换C语言呢?