Skip to content

Golang在嵌入式开发的调研

支持的ARM架构[1]

架构状态GOARM值GOARCH值
ARMv4 及以下不支持
ARMv5支持GOARM=5GOARCH=arm
ARMv6支持GOARM=6GOARCH=arm
ARMv7支持GOARM=7GOARCH=arm
ARMv8支持GOARCH=arm64

可以使用 go tool dist list 命令来查看Go支持的目标平台和架构,也可以使用 go tool dist list -json 命令查看更详细的信息。

操作系统要求[1]

  • ARM on Linux:操作系统必须使用 EABI 内核。对于armel设备与ARMv5兼容,armhf设备与ARMv6及更高处理器架构兼容。
  • ARM on FreeBSD, OpenBSD, and NetBSD:需要ARMv6K处理器架构或更高

提示与技巧[1]

  1. golang在编译与测试时大量使用/tmp目录,如果你的/tmp目录挂在SD卡上,会对SD造成大量损耗,可以使用export TMPDIR使golang构建工具使用其他目录。
  2. 从源码编译构建golang需要至少256mb内存。
  3. 因为golang在运行测试时会大量创建线程,所以如果测试时因为资源不足测试失败的话,可以减少最大线程栈大小。比如使用ulimit -s 1024将其设置为1mb。

编译[2]

Go默认使用静态编译,所以编译出来的二进制文件很大。我们可以稍作处理在一定程度上减小程序大小。最简单的就是设置编译选项来减少某些内容来减少程序大小:

bash
go build -ldflags="-s -w"
# -s:忽略符号表和调试信息
# -w:忽略DWARFv3调试信息,使用该选项后将无法使用gdb进行调试
# 也可以使用strip消除Go的编译信息

也可以使用UPX工具,其原理是将打包出来的二进制文件进行压缩,然后在头部加入解压缩指令,所以体积上可以减小非常多,不过换来的代价是程序启动的时候解压会有少许时间消耗。另外upx也有其可适用的目标平台,详细内容可以参考其自带的文档。

Go也可以动态编译,不过那样用起来就麻烦了。查了一下,动态编译内容很少,而且用起来就比较麻烦了。

交叉编译[3]

GO OS / GO ARCHamd64386armarm64ppc64leppc64mips64lemips64mipslemipswasmrisc64
aix
android
darwin
dragonfly
freebsd
illumos
ios
js
linux
netbsd
openbsd
plan9
solaris
windows

Go和C语言的比较[3]

要求CC++Go
设备存储要求最低低(多出1.8MB)低(多出2.1MB)
Yocto中的安装要求
缓冲器不足/溢出保护轻度
CFEngine代码重用/共享简单(完全向后兼容)可以导入C接口
自动内存管理可用,但非强制启用
标准数据容器
JSONjson-Cjsoncpp内置
HTTP库curlcurl内置
SSL/TLSOpenSSLOpenSSL内置

评估团队使用Yocto创建一个完善的定制Linux映像,比较了原始的映像体积较之具有网络堆栈的映像体积,以及最终应用程序写入映像的方式。

CC++C++/QtGo
以最小可用Yocto映像为基础测试所有组件和库的大小8.4MB10.2MB20.8MB14.6MB
网络堆栈大小13.4MB(curl)15.2MB(curl)20.8MB14.6MB
共享依赖关系
需要额外的Yocto层
部署复杂性二进制二进制二进制+Qt二进制

使用最基本的“hello world” 代码进行体积比较:

Golang:

go
package main

import "fmt"

func main() {
	fmt.Println("hello,world!")
}


$ go build
     > 1.9MB
$ go build -ldflags ‘-s -w’
     > 1.3MB

C:

C
#include <stdio.h>

int main() {
    printf("Hello, World!
");
    return 0;
}

$ gcc main.c -o main
     > 49KB

C++:

c++
#include <iostream>

int main() {
    std::cout << "Hello, World!" << std::endl;
    return 0;
}

$ g++ main.c -o main
     > 56KB