当使用CGO_ENABLED是必须的,会发生什么

41ik7eoe  于 2024-01-04  发布在  Go
关注(0)|答案(2)|浏览(324)

提问

请帮助理解什么时候CGO_ENABLED是必须的和不可避免的,以及go编译过程会发生什么。

CGO_ENABLED必须为时

在阅读了references中的文章后,似乎只要go对目标平台有交叉编译的原生支持,CGO_ENABLED就永远不是必须的。这是正确的吗?

cgoCGO_ENABLED为absolut-must的情况是go编译器无法生成目标平台的二进制代码。例如,假设我正在用Go编写一个Space X控制板嵌入式程序,我必须使用C库和该板的编译器。该板没有其他库或编译器可用。

在这种情况下,我必须做CGO_ENABLED=1,并告诉cgo要使用的编译器和链接器,以及C库二进制文件在我的笔记本电脑中的复制位置,并设置编译器CFLAGS等。但不知何故,我在这种情况下,我必须使用Go。这是当CGO_ENABLED=1将是必须的。这是正确的吗?
否则,如果especially go支持目标平台交叉编译,那么使用CGO_ENABLED=1可能是一条捷径,只需在目标平台上重用现有的C库。这是正确的吗?还是有其他原因需要CGO_ENABLED

发生了什么

我假设当使用CGO_ENABLED=1时,基本上会有一个来自Go部分的二进制文件和来自C库的二进制文件在go build命令创建的可执行文件中的组合。并且在运行时,执行会在go和C两个二进制世界之间来回移动。Go二进制端不会知道C端,所以像debugger这样的go工具将无法使用。
我认为可执行文件是硬链接所有库还是使用动态链接取决于目标平台编译器。
这些理解是否正确?
交叉编译默认设置CGO_ENABLED=0的原因是我们应该使用目标平台交叉编译的go内置支持,没有理由不这样做。
对于本机本地平台,默认情况下CGO_ENABLED=1的原因是因为cgo编译器知道(或者编译器的作者知道)本地机器架构和OS库(或众所周知的第三方)可用,因此可以预期最佳优化?但我不确定这是真的,因为go编译器本身可以针对本地操作系统和架构进行最佳优化,那么为什么需要使用cgo?
请解释为什么本地平台默认情况下CGO_ENABLED=1

参考资料

有些人遇到问题时会想:“我知道,我会用cgo。”现在他们有两个问题。
cgo是一项令人惊叹的技术,它允许Go程序与C库进行互操作。这是一个非常有用的功能,如果没有它,Go就不会有今天的地位。cgo是在Android和iOS上运行Go程序的关键。
然而,要明确的是,这些都是我的观点,我不代表任何人说话,我认为cgo在Go项目中被过度使用。我相信,当在Go中重新实现一大块C代码时,程序员选择使用cgo来 Package 库,认为这是一个更容易处理的问题。我相信这是一种虚假的经济。
显然,在某些情况下cgo是不可避免的,最明显的是当你必须和一个图形驱动或者窗口系统交互时,它们只能作为二进制blob使用,但是那些cgo的使用证明了它的权衡的情况比许多人愿意承认的要少得多。
Go对交叉编译的支持是同类中最好的。从Go 1.5开始,你可以通过Go项目网站上的官方安装程序从任何支持的平台交叉编译到任何其他平台。
默认情况下,cgo在交叉编译时是禁用的。如果你的项目是纯Go,这通常不是问题。当你混合了对C库的依赖时,你要么必须给予交叉编译你的产品的选项,要么你必须投入时间为所有目标寻找和维护交叉编译C工具链。
Go支持的平台数量持续增长。Go 1.5增加了对64位ARM和PowerPC的支持。Go 1.6增加了对64位MIPS的支持,IBM的s390架构被吹捧为Go 1.7。RISC-V正在开发中。如果你的产品依赖于C库,你不仅会遇到上面描述的交叉编译的所有问题,你还必须确保你所依赖的C代码在Go支持的新平台上可靠地工作--而且你必须在C/Go混合提供给你的有限的可调试性的情况下做到这一点。

“参考”Go实现的编译器(历史上被称为“gc”;可以从主站点下载)默认生成静态链接的二进制文件。这意味着,这样的二进制文件只依赖于操作系统内核提供的所谓“系统调用”,而不依赖于操作系统(或第三方)提供的任何共享库。
在基于Linux的平台上,这并不完全正确:在默认设置中(在Linux上为Linux构建,即不交叉编译),生成的二进制文件实际上与libc和libpthread(间接地,通过libc)链接。

cgo工具默认情况下会为系统上的本机构建启用。交叉编译时默认情况下会禁用它。您可以通过在运行go工具时设置CGO_ENABLED环境变量来控制这一点:将其设置为1以启用cgo的使用,将其设置为0以禁用它。如果启用了cgo,go工具将设置构建约束“cgo”。
交叉编译时,必须为cgo指定一个C交叉编译器。可以通过在使用make.bash构建工具链时设置CC_FOR_TARGET环境变量,或者在运行go工具时设置CC环境变量来实现这一点。CXX_FOR_TARGET和CXX环境变量的工作方式与C++代码类似。

fnatzsnv

fnatzsnv1#

cgo有它的缺点,但它可以是一个很好的权衡。甚至标准库也使用它(net用于DNS查找,os/user用于用户查找),因为它没有重新实现100%的Go行为。
交叉编译C代码仍然相当困难;你需要目标架构的C编译器和工具链(例如CC=aarch64-linux-musl-gccgo build来构建一个arm 64二进制文件)。这些都不是默认安装的,所以对于大多数人来说,cgo在交叉编译时根本不起作用;他们需要首先手动设置它。
cgo通常不是严格要求的(比如在net和os/user包中),所以默认禁用它似乎是最友好的选择。
但是在本机平台上没有这样的约束,并且它在默认情况下不需要任何用户设置就可以工作;那么为什么不默认启用它呢?

unftdfkk

unftdfkk2#

如果你运行的是Alpine镜像,那么就不可能立即在Alpine镜像中编译和运行Go程序。你必须通过将环境变量CGO_ENABLED设置为false(默认值为true)来禁用CGO。
您可以通过以下方式实现此目的:

  • 添加go env -w CGO_ENABLED=0,就像罗伯特在他的评论中提到的那样,
  • go命令中设置env值前缀,例如:CGO_ENABLED=0 GOARCH=amd64 GOOS=linux go build -ldflags '-s -w' -tags lambda.norpc -o bin/<YOUR_FUNC>/bootstrap <YOUR_PATH>/main.go

参见:https://megamorf.gitlab.io/2019/09/08/alpine-go-builds-with-cgo-enabled/

相关问题