cgo 和 Go 语言是两码事 已翻译 100%

oschina 投递于 2016/01/20 11:16 (共 8 段, 翻译完成于 01-29)
阅读 12675
收藏 53
Go
7
加载中

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 库的时候,你可能没有意识到你的这种权衡其实是不完整的。

终日乾乾
终日乾乾
翻译于 2016/01/21 17:19
2

构建时间变长

当你在包中引用 import "C",go build 就会做很多额外的工作来构建你的代码,构建就不会仅仅是向 go tool compile 传递一堆 .go 文件了,取而代之的是:

  • cgo 工具就会被调用,在 C 转换 Go、Go 转换C的之间生成各种文件。

  • 你系统的 C 编译器会被调用来处理你包中所有的C文件。

  • 所有独立的编译单元会被组合到一个 .o 文件。

  •  生成的 .o 文件会在系统的连接器中对它的引用进行一次检查修复。

当你针对这个包编程的时候,所有上面的工作在你编译或者测试的过程中都会进行。Go 工具在可能的情况下,会并行的处理这些工作,但是这个包的编译时间会随着所有 C 代码的构建而增加。

你可以将这部分 C 代码重构出这个包来解决,但是如果你不用 cgo,自然也就不会遇到这个问题。

对了,你还必须调试 C 代码在不同平台的兼容性问题。

drkaka
drkaka
翻译于 2016/01/21 09:08
0

复杂的构造

Go的目标之一是生产一种语言,它的构造过程就是它自我描述的过程;你程序的资源包含了足够的信息来使用一个工具去创建这个项目。这不是说用 Makefile 来自动化构建工程是不好的,但是在cgo被引进项目之前,你创建并且测试不需要任何东西,除了go工具。后来cgo被引入,设置所有的环境变量、跟踪那些可能被安装在奇怪地方的共享对象和头文件,现在这些你都需要做。

记住,Go支持那些不装载开箱即用的平台,所以你不得不花一些时间去提出一个Windows用户的解决方案。

还有,现在你的用户还要安装C编译器,而不是只安装Go编译器。他们同时还需要安装你项目所依赖的C库,所以你同时还要承担这些支持的成本。


终日乾乾
终日乾乾
翻译于 2016/01/26 16:47
1

交叉编译不支持

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混合提供的有限的调试。这又引出了下面的问题。

终日乾乾
终日乾乾
翻译于 2016/01/27 14:42
1

你失去了通向你所有工具的入口

Go有一些伟大的工具;有竞态分析、性能分析、覆盖率、模糊测试和其他源代码分析工具。这些中没有任何一个能够跨越cgo血液或是cgo大脑的屏障。

相反地,像Valgrind这样的优秀工具不理解Go的调用约定和堆栈布局。在这一点上,Ian Lance Taylor的在Go 1.6中将C的内存清理和空指针调试相结合的工作对于cgo的用户来说将会非常有益。

梳理Go的代码和C的代码结果到了两个世界的交汇处,不是结盟;C的内存安全,Go程序的可调试性。

性能永远是一个问题

C代码和Go代码生存在两个不同的宇宙中,cgo横贯了他们两个之间的分界线。这个过渡并不是自由的,它取决于它在你代码中的位置,花费可能是无足轻重的,也可能是巨额的。

终日乾乾
终日乾乾
翻译于 2016/01/29 10:19
1

C 并不了解 Go 的调用协定或堆栈,所以 Go 语言调用到 C 代码时必须首先记录 Go 函数入口堆栈的所有细节,然后切换到 C 的堆栈,运行 C 代码,这部分 C 代码并不知道它如何被调用,更不知道外部的 Go 语言运行时环境。

公平地说,Go 也不知道任何关于 C 的情况。这就是为什么随着编译器和垃圾收集器在定位无用栈帧和堆方面的发展,Go 和 C 两者之间传递数据的规则变得越来越复杂。

如果在 C 的运行过程中出现一个错误,Go 至少得能做到打印错误堆栈信息,然后退出程序,而不是把核心文件都暴露出来。

管理这种双方互相调用的堆栈,再加上在信号、线程和回调,真的很是不容易的。Ian Lance Taylor 在1.6版本的 Go 语言中做了大量的工作来改进与 C 语言信号处理方面的互操作性。

这里要说的是 C 与 Go 语言之间的的互相调用是比较繁琐的,而且永远是会有性能开销的。

雪落无痕xdj
翻译于 2016/01/28 21:43
0

C 是主导,而不是你的代码

你用那种语言捆绑或包装C代码都没关系;Python、有JNI的Java、一些使用了libFFI的语言,或者是有cgo的Go;这是C的世界,你只是在其上生存。

Go代码和C代码必须在资源共享方式上取得一致,如地址空间、信号处理和线程调度——而我所说的一致,是Go要围绕着C的假设。 C代码可以假设它一直在一个线程上运行,或者是无顾虑地在没有任何准备工作的情况下直接运行在有许多线程的环境中。

你不是在写一个Go程序使用C库中的一些逻辑,取而代之的是你在写一个Go程序,它必须与一段容易冲突的、很难被取代的、很难协商的、不顾虑你的问题的C代码共存。

终日乾乾
终日乾乾
翻译于 2016/01/28 13:57
1

部署变得更复杂

对于普通读者来说,任何关于Go的描述都要包含至少以下词汇中的一点:

简单,静态二进制

这是Go的法宝,它引领Go成为了远离虚拟机和运行管理的典型代表。使用cgo,你就要放弃这些。

根据你的环境,把你的Go项目打成一个deb或rpm包,并且假设你其他的依赖也包装好了,把它们加进安装依赖,然后把问题从操作系统的包管理中抛出,这也许是可行的。但是这几个构建和部署程序的重要改变就像 go build && scp一样仓促直白。

编译一个完全静态的Go程序是可行的,但是如果你的项目中包含了cgo那将是绝不简单的,其后果将影响一整个构建和部署的生命周期。

请明智地选择

要明确,我不是说你不应该使用cgo。但是在你做这笔交易之前,请仔细考虑你同时要放弃的Go的优点。

相关文章:

  1. Go 1.1的交叉编译介绍

  2. Go 1.5的交叉编译

  3. 交叉编译变得更完整在Go 1.5中

  4. Go的交叉编译介绍

终日乾乾
终日乾乾
翻译于 2016/01/28 16:43
1
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接。
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
加载中

评论(27)

我勒个去啊汗
我勒个去啊汗

引用来自“eechen”的评论

@a3509007 ext_skel生成扩展骨架,phpize生成configure,然后就是传统的三部曲configure/make/make install,多么的C风格呀,还不服.反正PHP内置了SQLite,不服赶紧用你说的两三行代码来演示下Node和Java是如何使用SQLite这个共享库的嘛,让大家开开眼界呀,少装逼才好呢.
内置是别人帮你解决了问题,你也有脸出来说?你把傻逼方法说的头头是道是脑子有问题还是学的太少?我自己封个库只要几行代码就能操作SQLITE,你知道底层有多少处理?你是多无知才敢说出PHP和C亲和的话?PHP近代版本都是照着C++模仿的OK?我真服了有些人,只会PHP天天瞎逼逼,学个GO扯到PHP,自己看看PHP跌成什么狗了,还TM年度最佳语言,我也是呵呵了
东东-
东东-

引用来自“__JM_Joy__”的评论

像nim,julia这些语言可以直接调用C库,还可以编译成C语言,比cgo高明多了。
go的目的就是替代C,奈何库不够多。
julia 哪有可以编译成 C 啊? 新功能? cgo 是挺挫的. 不好
乐悠族
乐悠族
GO是未来的WEB语言,嗯,是未来的,不是现在的。
fy0
fy0

引用来自“a3509007”的评论

@eechen 想想也蛮搞笑的, openresty的核心都拿luajit的FFI重构完看不见抽调nginx API的C封装的中间lua模块了, 某言必称春哥挂虎皮套近乎拿openresty安利来安利去的二傻居然不知道还在要两行文件...................................我说你安利的东西你从来不看的是吧?
兄弟,支持你!简单明快有干货的打脸!
a
a3509007
@eechen 想想也蛮搞笑的, openresty的核心都拿luajit的FFI重构完看不见抽调nginx API的C封装的中间lua模块了, 某言必称春哥挂虎皮套近乎拿openresty安利来安利去的二傻居然不知道还在要两行文件...................................我说你安利的东西你从来不看的是吧?
a
a3509007

引用来自“eechen”的评论

@a3509007 说到最后,你还是没贴出你所谓的"几行代码调用C库"的示例,你还有什么好吹嘘的呢?反观php-src/ext的C扩展的PECL C扩展,已经说明了一切.20年后,Lerdorf回忆说他以为自己发布的是一个C API,结果完全不是那回事,否则现在人人都是C程序员了.PHP初衷并非设计成一门语言,而是设计一套帮助Web开发者访问底层C库的API,直到3.0之后PHP才加入越来越多的语言特性.
不要转移话题, 我没和你争论php好还是烂, 我针对的是你说php和C亲和性好这个睁眼瞎话的言论, 你有时间在这里贴搜到的安利文, 我给你的关键词你早能找到一堆了. python一行C没写用标准库里的ctypes调用tidy库 https://github.com/countergram/pytidylib/blob/master/tidylib/tidy.py 你自己数数php标准版里tidy扩展有多少文件吧. pypy一行C没写用cffi调用sqlite库 https://bitbucket.org/pypy/pypy/src/a71c1f3776a9ea0a64f54cf802c0f03e02c26c9a/lib_pypy/_sqlite3.py quora用ctypes封装的libmysqliclient, 一行C都没有哦 https://github.com/quora/mysql-ctypes. 你喜欢安利给别人的openresty系列 https://github.com/openresty/lua-resty-core/tree/master/lib "lua-resty-core - New FFI-based Lua API for the ngx_lua module" 里面现在可没C啊, 你说你这都不知道没事给人安利个毛线啊? java的JNI案例不多说了, android开发里NDK用得多的一逼. 嗯, 贵大php的FFI项目 https://pecl.php.net/package/ffi 可惜难产了, 不然也不至于用傻逼傻逼的生成扩展的方式来调用链接库了, 可怜这方面缺陷的这么明显还有二缺当优点来安利.
eechen
eechen
@a3509007 说到最后,你还是没贴出你所谓的"几行代码调用C库"的示例,你还有什么好吹嘘的呢?反观php-src/ext的C扩展的PECL C扩展,已经说明了一切.20年后,Lerdorf回忆说他以为自己发布的是一个C API,结果完全不是那回事,否则现在人人都是C程序员了.PHP初衷并非设计成一门语言,而是设计一套帮助Web开发者访问底层C库的API,直到3.0之后PHP才加入越来越多的语言特性.
mikeszhang
mikeszhang
看来PHP脑残粉连老祖宗语言C都敢撕,我Java神教都要让C三分啊。。。。
huangyanxiong
huangyanxiong

引用来自“eechen”的评论

@a3509007 几行代码都不愿意贴出来,你还装什么装,就凭你这种眼高手低的装逼犯,还想黑PHP的C扩展能力,真是不要脸.

引用来自“a3509007”的评论

首先, 我不在装B, 我举的任何一个语言的FFI原生实现或者扩展都不是我写的, 我拿什么装B? 拿laruence认个哥么? 可惜你鸟哥不知道你是哪根葱老拿他出来装蒜. 你心态太畸形了. 其次, 你就一加减乘除的水准不理解代数关我毛事, 我收你学费了要贴代码给你? 给你关键词算仁至义尽了, 任何一个关键词都能找到官方文档和案例, 你当像php一样偷偷摸摸加了sapi的embed实现还半页文档都不给么? 再说, 你贴代码了么? 别想打人脸先把自己给抽上. 第三, php调用动态链接库的流程不是准备好源代码+gcc+automake+autoconf一堆工具>填bridge>phpize>configure>make, 你要说php看不上FFI喜欢纯手工打造那也就算了, 问题是pecl有FFI实现, 也太监十多年了, 我哪里黑了? 我哪里说错了? 如果之前忘说准备的工具里还需要libtool和bison等等, 那我道歉好了......
他是PHP的未来
a
a3509007

引用来自“eechen”的评论

@a3509007 几行代码都不愿意贴出来,你还装什么装,就凭你这种眼高手低的装逼犯,还想黑PHP的C扩展能力,真是不要脸.
首先, 我不在装B, 我举的任何一个语言的FFI原生实现或者扩展都不是我写的, 我拿什么装B? 拿laruence认个哥么? 可惜你鸟哥不知道你是哪根葱老拿他出来装蒜. 你心态太畸形了. 其次, 你就一加减乘除的水准不理解代数关我毛事, 我收你学费了要贴代码给你? 给你关键词算仁至义尽了, 任何一个关键词都能找到官方文档和案例, 你当像php一样偷偷摸摸加了sapi的embed实现还半页文档都不给么? 再说, 你贴代码了么? 别想打人脸先把自己给抽上. 第三, php调用动态链接库的流程不是准备好源代码+gcc+automake+autoconf一堆工具>填bridge>phpize>configure>make, 你要说php看不上FFI喜欢纯手工打造那也就算了, 问题是pecl有FFI实现, 也太监十多年了, 我哪里黑了? 我哪里说错了? 如果之前忘说准备的工具里还需要libtool和bison等等, 那我道歉好了......
返回顶部
顶部