迫在眉睫的截止日期、不切实际的时间表,以及在不影响质量的情况下按时开发和发布应用程序的无尽压力。是不是很熟悉的感觉?嵌入式开发团队尽快实现嵌入式应用的发布是非常重要的。那么,在优先考虑应用程序质量和安全性的同时,是否有快速交付应用程序的捷径?在这个思路下,OTA软件更新管理软件开发团队Mender.io对Mender嵌入式客户端和服务器部分开发的最佳编程软件进行了评测,Go、C、C++入围。最终选择了Go。接下来,我们将通过评测过程中的优劣判断对比,分析围棋能够获胜的原因。虽然这种选择有些主观,但Go是一种非常高效的嵌入式开发语言,尤其是在网络编程方面。网络编程在某种程度上遍及每个连接的设备或应用程序。Go语言是由谷歌创建的,最初是为了满足谷歌开发人员在其生态系统中面临爆炸式复杂性的需求。因此,GO语言从一开始就遵循高效编译、高效执行、易编程的三大原则,这是它相对于其他主流语言的优势所在。但是,需要强调的是,Go不能被认为是C语言的替代品。在很多情况下,C语言仍然是不可替代的,比如实时操作系统和设备驱动程序的开发。嵌入式开发的严格要求一旦架构建立,Mender产品工程团队就开始评估哪种语言最适合开发Mender应用程序。该系统由两部分组成:一是运行在嵌入式设备上的客户端,二是连接各个客户端的中央服务器。因此,该语言有如下要求:客户端应用程序运行在嵌入式设备上,要求编译后的二进制文件尽可能小;可以兼容Linux的嵌入式发行版Yocto。客户端应该易于安装并且不依赖于外部项目或库。由于需要在不同的设备上运行,语言必须具有跨架构的编译能力。运行Mender客户端应用程序的设备将是物联网或网络设备,因此该语言需要能够访问网络库。此外,还有一些非功能性需求:该语言能被本部门大多数程序员理解和掌握;与用C语言编写的现有应用程序共享和重用现有代码尽可能容易,客户端和服务器代码也可以在应用程序之间重用。还要考虑开发速度——团队不断面临快速添加新功能的压力。Go、C和C++的比较如图1所示。选择Go主要是因为它支持缓冲区溢出保护、自动内存管理、使用标准数据容器以及对JSON、HTTP和SSL/TLS库的集成支持。图1:Go、C和C++的特性表比较图像。在本次评测中,Go与C、C++和C++/Qt不相上下。由于Go应用程序可以静态编译成单个二进制文件,因此不需要任何额外的虚拟环境(就像Java除了二进制文件之外还需要虚拟机),并且Go代码不需要依赖其他项来运行设备。图2:Go与其他编程语言的对比GO和C选择Go语言进行开发也源于其极为丰富的标准库。这一优势有助于加快开发过程,尤其是与C语言相比。Go语言继承了C、C++和Python的许多元素,包括表达式语句、控制流语句、数据结构、接口、指针、按引用传递的概念、参数解析语法、字符串处理和垃圾收集机制。凭借其优化的编译器,Go使代码能够在嵌入式设备上本地运行。当然,Go语言难免有一些缺点,后面会提到。但是缺点并不能掩盖优点,这并不影响Go语言明显的优点,比如开发速度和语言构建的便利性。这些也对评测团队的选择产生了影响,下面我们一一来看。Go和C代码大小对比在代码大小方面,Go不如C轻,这是它为数不多的缺点之一。以最简单的应用程序“helloworld”为例,使用Go语言编写,使用内置的printIn函数,去除调试符号后大小仅600KB(代码片段1)。如果包含fmt包及其依赖项,则大小会增加到1.5MB(代码段2)。相比之下,使用C语言,如果建立一个动态链接库,其大小只有8KB。但如果使用静态链接库,大小会增加到800KB以上,这比Go二进制文件(第3段)大得惊人。代码片段一:最基本的Go语言“helloworld”代码:packagemainfuncmain(){println("helloworld")}$gobuild>938K$gobuild-ldflags'-s-w'>682K$gobuild&strip>623KSegment2:StandardGolanguage"helloworld"代码:packagemainimport"fmt"funcmain(){fmt.Println("helloworld")}$gobuild>1,5MSegment3:Clanguage"你好世界”代码:#includeintmain(void){printf("helloworld\n");返回0;}$gccmain.c>8,5K$ldda.out>linux-vdso.so.1>libc.so.6>/lib64/ld-linux-x86-64.so.2$gcc-staticmain.c>892K$gcc-staticmain.c&strip>821KGo用C编译的Go代码通常比C语言可执行文件慢。Go有完备的垃圾回收机制,自然影响运行速度。使用C语言时,可以精确指定为变量分配的内存位置,变量是在栈上还是在堆上;使用Go时,编译器会自动处理变量的分配位置。程序员可以看到变量分配到哪里(gobuild-gcflags-m),但不能强制编译器只使用某个位置,比如栈。但是速度不限于以上,还要考虑编译速度和开发速度。Go提供极快的编译速度。例如,15,000行的Go客户端编译仅需1.4秒。此外,Go是为并发执行而设计的(goroutines和channels)。上面介绍的丰富的标准库可以覆盖大部分的基本需求,有助于程序员加快开发速度。编译和交叉编译有两种编译器可供程序员选择使用:原始编译器称为gc,默认安装程序自带,由Google编写和维护。第二个叫gccgo,是GCC的前端。gccgo的编译速度在使用的时候是非常快的,大的模块通常几秒钟就可以编译完成。如前所述,Go默认编译为静态库,因此可以创建单个二进制文件而无需额外的依赖项或虚拟机。可以使用-linkshared标志创建和使用共享库。一般来说,Go不需要构建文件。如果需要构建,可以使用简单的“gobuild”命令执行。高级和复杂的构建可以使用Makefile。这是Mender中使用的Makefile。除此之外,Go在交叉编译方面支持众多操作系统和架构。图3说明了Go支持的各种操作系统和平台。图3Go支持的各种操作系统和平台Go中的调试和测试许多开发人员使用GDB来监控程序执行。对于高并发的Go应用,用GDB调试时会出现一些问题。幸运的是,一个名为Delve的专用Go调试器非常好用,确保即使只使用GDB的开发人员在大多数情况下也可以轻松调试。在Go代码中添加测试和单元测试非常简单,测试是Go语言内置的——只需创建一个带有“test”后缀的文件,在函数前加上test,导入测试包,然后运行“gotest”。然后所有测试将自动从源中获取并相应地执行。并发支持Go语言对并发支持特别友好。它有两个内置的调度机制:goroutines和channels。Goroutines是轻量级线程,只有2kB。创建起来非常简单:只需在函数前面加上“go”关键字,它就会并发执行。Go有自己的内部调度器,可以根据需要将goroutine多路复用到OS线程中。通道是用于在goroutine之间交换消息的“管道”,可以是阻塞的也可以是传递的。静态库和动态库:GoGo中的C代码有许多鲜为人知的特性,允许程序员使用特殊标志将指令发送到编译器、链接器和工具链的其他部分。其中包括-buildmode和-linkshared标志,这两个标志都可用于为Go和C代码段创建和使用静态和动态库。通过特定标志的组合,程序员可以使用生成的C语言头文件创建静态或动态库,这些头文件稍后可以被C代码调用。恩,那就对了!使用Go,您可以调用C代码并拥有两全其美的优势。Go发行版包括一个名为cgo的工具,它可以执行C代码,这使得重用程序员自己的或系统C库成为可能。事实上,间接引用Cgo和C系统库是很常见的。在某些情况下,标准C库例程是由Go语言构造语法本身从Go实例中挑选出来的。例如,在Unix系统上使用网络包时,很有可能会选择基于cgo的解析器。它调用C库例程(getaddrinfo和getnameinfo)来解析无法使用本机Go解析器解析的名称(例如在OSX上禁用直接DNS调用时);另一种常见情况是使用系统/用户包小时。如果想跳过构建cgo部分,程序员可以通过osusergo和netgo标签(gobuild-tagsosusergo,netgo)或使用CGO_ENABLED=0变量完全禁用cgo。将C和Go混合在一起时,还有一些更棘手的事情需要注意。由于Go有垃圾回收机制而C没有,如果在Go代码中使用分配在C代码栈上的任何C变量,则必须在Go中声明和释放。通过使用接口生成工具(SWIG),也可以在Go代码中使用C++。SWIGC能够将Go与C++和Python等语言集成。Go的优点大于缺点从以往的经验来看,可以得出结论,Go既有优点也有缺点。就负面因素而言,社区中外部库很多,但良莠不齐,需要非常小心区分和使用。随着Go语言的成熟和被广泛采用,这种情况正在迅速改善。此外,当Go代码中有一些C绑定时,事情通常会变得更加复杂,尤其是交叉编译,如果不导入整个C交叉编译基础设施,这很难做到。总的来说,使用Go进行嵌入式开发还是有很多优势的。从C或Python(或两者)过渡到Go并在短期内使用它是快速、简单且可行的。Go语言提供了优秀的工具和包含100多个包的标准库。程序员可以轻松控制和设置运行时参数,例如要使用的操作系统线程数(GOMAXPROCS)、打开或关闭垃圾收集(GOGC=off)等。库和代码可以在服务器和客户端开发团队之间轻松共享,如本文中提到的示例。这么说可能会引起争议,但一致的编码标准实际上可以加快开发速度。无论程序员检查什么代码,它看起来都是一致的。无需花时间考虑使用哪种编码标准。工程师们开发了一些可以应用于新兴设备(例如BeagleboneBlack或RaspberryPi)的嵌入式Go应用的demo:其中一个叫做“Thermostat”,基于Beaglebone硬件,使用红外距离、温度和湿度传感器,以及可以通过网络界面读取所有传感器读数;还有一种基于PRi的机器人汽车,带有可以通过网络界面控制的摄像头。喜欢就好好享受吧!译者介绍张哲刚,社区编辑,系统运维工程师,国内较早一批硬件评测和互联网从业者,曾加入阿里巴巴。十余年IT项目管理经验,具备复合型知识技能,曾参与多个网站架构设计、电子政务系统开发,领导运维某市级招生考试管理平台。原标题:ComparingGovs.Cinembeddedapplications,作者:MarcinPasinski