cgo 和 Go 语言是两码事
cgo不是Go
借用 JWZ的一句话
有些人,当他们面临一个问题时,认为“我知道,我会使用 cgo ”。那么现在,他们有了两个问题。
最近有人在 Gopher 的 Slack Channel 上使用 cgo,对此我感到十分担心,尤其是竟然有个组织内部打算用一个项目来展示 Go,那真是一个坏主意。对此,我曾说过很多次了,因此也许你们讨厌了我的游说,所以我想到了把它写下来并且去做。
cgo 是一个令人惊异的技术,它允许 Go 程序与 C 的类库交互操作。那是一个极其有用的特征,今天它达到了一个 Go 所无法企及的地位。cgo 是让 Go 程序在 Android 和 iOS 上运行的关键。
然而,这只是我的个人意见,我不为任何人说话,我认为 cgo 在 Go 项目中被过度使用了。我相信当面临需要重载一大段用 Go 编写的 C 代码时,程序员会更愿意选择用 cgo 去打包库而非 Go,因为他们认为那样更容易解决问题。我认为那是虚假经济。
显而易见的,cgo 也存在一些不可避免的问题,最明显的一个问题是作为一个二进制 blog,你不得不与显卡驱动或者窗口系统进行交互 。但是使用 cgo 所存在的问题,经过权衡后,大部分人认为还是比较少的。
当你在 Go 项目上建立一个 cgo 库的时候,你可能没有意识到你的这种权衡其实是不完整的。
构建时间变长
当你在包中引用 import "C",go build 就会做很多额外的工作来构建你的代码,构建就不会仅仅是向 go tool compile 传递一堆 .go 文件了,取而代之的是:
cgo 工具就会被调用,在 C 转换 Go、Go 转换C的之间生成各种文件。
你系统的 C 编译器会被调用来处理你包中所有的C文件。
所有独立的编译单元会被组合到一个 .o 文件。
生成的 .o 文件会在系统的连接器中对它的引用进行一次检查修复。
当你针对这个包编程的时候,所有上面的工作在你编译或者测试的过程中都会进行。Go 工具在可能的情况下,会并行的处理这些工作,但是这个包的编译时间会随着所有 C 代码的构建而增加。
你可以将这部分 C 代码重构出这个包来解决,但是如果你不用 cgo,自然也就不会遇到这个问题。
对了,你还必须调试 C 代码在不同平台的兼容性问题。
复杂的构造
Go的目标之一是生产一种语言,它的构造过程就是它自我描述的过程;你程序的资源包含了足够的信息来使用一个工具去创建这个项目。这不是说用 Makefile 来自动化构建工程是不好的,但是在cgo被引进项目之前,你创建并且测试不需要任何东西,除了go工具。后来cgo被引入,设置所有的环境变量、跟踪那些可能被安装在奇怪地方的共享对象和头文件,现在这些你都需要做。
记住,Go支持那些不装载开箱即用的平台,所以你不得不花一些时间去提出一个Windows用户的解决方案。
还有,现在你的用户还要安装C编译器,而不是只安装Go编译器。他们同时还需要安装你项目所依赖的C库,所以你同时还要承担这些支持的成本。
交叉编译不支持
Go对于交叉编译的支持是最好的。根据Go1.5交叉编译,你可以从任何支持的平台到其他任何平台,通过Go项目网站上的官方许可安装程序。
在默认情况下cgo是不允许交叉编译的。 通常这不构成问题,如果你的项目是纯Go的。 当你混合了C库的依赖, 你或者不得不放弃交叉编译你的产品,或者不得不花时间在寻找并且维护C的交叉编译工具链来达成你的目标。
或许你所做的产品仅通过TCP与客户端通信,并且你计划让它在SaaS(软件服务化 Software as a Service)的模型上运行,那么你根本不关心交叉编译就是合理的。然而,如果你在做一个其他人会用的产品,可能会整合到他们的产品中去,或许那是一个监控解决方案,或许那是一个你SaaS服务的客户端,那么你就把他们很容易交叉编译的特性给锁死了。
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有一些伟大的工具;有竞态分析、性能分析、覆盖率、模糊测试和其他源代码分析工具。这些中没有任何一个能够跨越cgo血液或是cgo大脑的屏障。
相反地,像Valgrind这样的优秀工具不理解Go的调用约定和堆栈布局。在这一点上,Ian Lance Taylor的在Go 1.6中将C的内存清理和空指针调试相结合的工作对于cgo的用户来说将会非常有益。
梳理Go的代码和C的代码结果到了两个世界的交汇处,不是结盟;C的内存安全,Go程序的可调试性。
性能永远是一个问题
C代码和Go代码生存在两个不同的宇宙中,cgo横贯了他们两个之间的分界线。这个过渡并不是自由的,它取决于它在你代码中的位置,花费可能是无足轻重的,也可能是巨额的。
C 并不了解 Go 的调用协定或堆栈,所以 Go 语言调用到 C 代码时必须首先记录 Go 函数入口堆栈的所有细节,然后切换到 C 的堆栈,运行 C 代码,这部分 C 代码并不知道它如何被调用,更不知道外部的 Go 语言运行时环境。
公平地说,Go 也不知道任何关于 C 的情况。这就是为什么随着编译器和垃圾收集器在定位无用栈帧和堆方面的发展,Go 和 C 两者之间传递数据的规则变得越来越复杂。
如果在 C 的运行过程中出现一个错误,Go 至少得能做到打印错误堆栈信息,然后退出程序,而不是把核心文件都暴露出来。
管理这种双方互相调用的堆栈,再加上在信号、线程和回调,真的很是不容易的。Ian Lance Taylor 在1.6版本的 Go 语言中做了大量的工作来改进与 C 语言信号处理方面的互操作性。
这里要说的是 C 与 Go 语言之间的的互相调用是比较繁琐的,而且永远是会有性能开销的。
C 是主导,而不是你的代码
你用那种语言捆绑或包装C代码都没关系;Python、有JNI的Java、一些使用了libFFI的语言,或者是有cgo的Go;这是C的世界,你只是在其上生存。
Go代码和C代码必须在资源共享方式上取得一致,如地址空间、信号处理和线程调度——而我所说的一致,是Go要围绕着C的假设。 C代码可以假设它一直在一个线程上运行,或者是无顾虑地在没有任何准备工作的情况下直接运行在有许多线程的环境中。
你不是在写一个Go程序使用C库中的一些逻辑,取而代之的是你在写一个Go程序,它必须与一段容易冲突的、很难被取代的、很难协商的、不顾虑你的问题的C代码共存。
部署变得更复杂
对于普通读者来说,任何关于Go的描述都要包含至少以下词汇中的一点:
简单,静态二进制
这是Go的法宝,它引领Go成为了远离虚拟机和运行管理的典型代表。使用cgo,你就要放弃这些。
根据你的环境,把你的Go项目打成一个deb或rpm包,并且假设你其他的依赖也包装好了,把它们加进安装依赖,然后把问题从操作系统的包管理中抛出,这也许是可行的。但是这几个构建和部署程序的重要改变就像 go build && scp一样仓促直白。
编译一个完全静态的Go程序是可行的,但是如果你的项目中包含了cgo那将是绝不简单的,其后果将影响一整个构建和部署的生命周期。
请明智地选择
要明确,我不是说你不应该使用cgo。但是在你做这笔交易之前,请仔细考虑你同时要放弃的Go的优点。
相关文章:
cgo 和 Go 语言是两码事的更多相关文章
- CGO封装C语言qsort函数
封装qsort函数 package qsort /* #include <stdlib.h> typedef int (*qsort_cmp_func_t) (const void* a, ...
- golang cgo 使用总结
原文地址 CGO 提供了 golang 和 C 语言相互调用的机制.某些第三方库可能只有 C/C++ 的实现,完全用纯 golang 的实现可能工程浩大,这时候 CGO 就派上用场了.可以通 CGO ...
- <2014 05 21> 互联网时代的C语言——Go(2)
**************************************************************************************************** ...
- 09. Go 语言并发
Go 语言并发 并发指在同一时间内可以执行多个任务.并发编程含义比较广泛,包含多线程编程.多进程编程及分布式程序等.本章讲解的并发含义属于多线程编程. Go 语言通过编译器运行时(runtime),从 ...
- Golang基于学习总结
1.不支持继承 重载 ,比方C++Java的接口,接口的改动会影响整个实现改接口的类行为的改动,Go 设计者觉得这一特点也许根本没用. 2.必不论什么函数定义必须花括号跟在函数声明后面而不能换行 如 ...
- deno深入揭秘及未来展望
deno node.js之父Ryan Dahl在一个月前发起了名为deno的项目,项目的初衷是打造一个基于v8引擎的安全的TypeScript运行时,同时实现HTML5的基础API.所谓的安全运行时, ...
- Golang基础学习总结
转自:http://blog.csdn.net/yue7603835/article/details/44264925 1.不支持继承.重载 ,比如C++.Java的接口,接口的修改会影响整个实现改接 ...
- Gogeos安装
环境要求: Windows64,Go,minGW(统一64位) 1.安装geos 下载GEOS 3.3.8源码,解压后,按readme文件编译(基于VS2010的64位编译工具执行的nmake编译命令 ...
- Go -- 通过GOTRACEBACK生成程序崩溃后core文件的方法(gcore gdb)
写一个错误的c程序 package dlsym import "testing" func Test_intercept(t *testing.T) { Intercept(& ...
随机推荐
- SCP-bzoj-1085
项目编号:bzoj-1085 项目等级:Safe 项目描述: 戳这里 特殊收容措施: A*(上下界剪枝). 答案上界:15. 答案下界:当前步数+当前状态剩余步数估价. 这里我们简单地设计估价函数为当 ...
- Linux Bash Shell快速入门(一)
BASH 的基本语法· 最简单的例子 —— Hello World! · 关于输入.输出和错误输出 · BASH 中对变量的规定(与 C 语言的异同) · BASH 中的基本流程控制语法 · 函数的使 ...
- MSSQLSERVER错误1
错误信息 数据库日志记录 09/07/2017 16:10:31,登录,未知,Login failed for user 'NT AUTHORITY\SYSTEM'. 原因: 无法打开明确指定的数 ...
- JS基础入门篇(二十四)—DOM(上)
1.常用的节点类型,nodeType,attributes,childNodes. 1.元素节点 - 1 2.属性节点 - 2 3.文本节点 - 3 4.注释节点 - 8 5.文档节点 - 9 查看节 ...
- css3动画的性能优化
目前对提升移动端CSS3动画体验的主要方法有几点:尽可能多的利用硬件能力,如使用3D变形来开启GPU加速 -webkit-transform: translate3d(0, 0, 0); -moz-t ...
- 查看静态库(.lib)和动态库(.dll)的导出函数的信息 error LNK2001: 无法解析的外部符号 _Delete
转自VC错误:http://www.vcerror.com/?p=1381 在window下查看动态库的导出函数可以用vs自带的Dependenc工具: 查看静态库的信息要用命令行来实现: 首先运行V ...
- 5、java操作xml,dom4j
. 1.首先在项目路径下引入dom4j-1.6.1.jar和jaxen-1.1-beta-6.jar包,jaxp方式解析xml文件 <?xml version="1.0" e ...
- spring boot starter开发
作为公司的技术保障部,一直承担着技术方向的把控,最近公司准备全面转入spring boot的开发.所以我们部门也一直在调研相关的技术知识点: 使用springboot开发应用已经有一段时间了,我们都沉 ...
- numpy 中文手册
https://yiyibooks.cn/xx/NumPy_v111/user/index.html
- Nginx网络架构实战学习笔记(六):服务器集群搭建、集群性能测试
文章目录 服务器集群搭建 Nginx---->php-fpm之间的优化 302机器 202机器 压力测试 搭建memcached.mysql(数据准备) 今晚就动手-.- 集群性能测试 服务器集 ...