开源中国

我们不支持 IE 10 及以下版本浏览器

It appears you’re using an unsupported browser

为了获得更好的浏览体验,我们强烈建议您使用较新版本的 Chrome、 Firefox、 Safari 等,或者升级到最新版本的IE浏览器。 如果您使用的是 IE 11 或以上版本,请关闭“兼容性视图”。
cgo 和 Go 语言是两码事 - 技术翻译 - 开源中国社区

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

英文原文:cgo is not Go
标签: Go
oschina 推荐于 3年前 (共 8 段, 翻译完成于 01-29) 评论 27
收藏  
53
推荐标签: 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 代码在不同平台的兼容性问题。

drkaka
 翻译得不错哦!

复杂的构造

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 语言之间的的互相调用是比较繁琐的,而且永远是会有性能开销的。

雪落无痕xdj
 翻译得不错哦!

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的优点。

相关文章:

  1. Go 1.1的交叉编译介绍

  2. Go 1.5的交叉编译

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

  4. Go的交叉编译介绍

终日乾乾
 翻译得不错哦!
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们
评论(27)
Ctrl/CMD+Enter

还是PHP跟C的亲和性好,执行 php-src/ext/ext_skel 就可以生成PECL C扩展源码骨架,php-src/ext/下还有各种现有扩展作为例子,会点C语言的都可以用C给PHP写API了.http://wiki.swoole.com/wiki/page/238.html

引用来自“eechen”的评论

还是PHP跟C的亲和性好,执行 php-src/ext/ext_skel 就可以生成PECL C扩展源码骨架,php-src/ext/下还有各种现有扩展作为例子,会点C语言的都可以用C给PHP写API了.http://wiki.swoole.com/wiki/page/238.html
php这辈子是不太可能编译成动态链接库让c去调用了。
像nim,julia这些语言可以直接调用C库,还可以编译成C语言,比cgo高明多了。
go的目的就是替代C,奈何库不够多。

引用来自“eechen”的评论

还是PHP跟C的亲和性好,执行 php-src/ext/ext_skel 就可以生成PECL C扩展源码骨架,php-src/ext/下还有各种现有扩展作为例子,会点C语言的都可以用C给PHP写API了.http://wiki.swoole.com/wiki/page/238.html
php是1994年出第一个版本,go是2012年出第一个版本, 今年是2016年,go才发展四年,eechen想想1998年的php,

引用来自“eechen”的评论

还是PHP跟C的亲和性好,执行 php-src/ext/ext_skel 就可以生成PECL C扩展源码骨架,php-src/ext/下还有各种现有扩展作为例子,会点C语言的都可以用C给PHP写API了.http://wiki.swoole.com/wiki/page/238.html

引用来自“nokia”的评论

php是1994年出第一个版本,go是2012年出第一个版本, 今年是2016年,go才发展四年,eechen想想1998年的php,
PHP跟Apache和MySQL都诞生于1995年.2004年的时候PHP已经是年度编程语言.2015年底发布的高性能PHP7又给PHP注入了新的活力.Go有Google这座靠山,又有原贝尔实验室的大牛Rob Pike和Ken Thompson坐阵,有这么雄厚的资源,四五年的时间可不算短了.对比看看PHP之父Rasmus用Perl和C搞自己的主页,再慢慢开发PHP,真可谓一穷二白呀.

引用来自“eechen”的评论

还是PHP跟C的亲和性好,执行 php-src/ext/ext_skel 就可以生成PECL C扩展源码骨架,php-src/ext/下还有各种现有扩展作为例子,会点C语言的都可以用C给PHP写API了.http://wiki.swoole.com/wiki/page/238.html

引用来自“nokia”的评论

php是1994年出第一个版本,go是2012年出第一个版本, 今年是2016年,go才发展四年,eechen想想1998年的php,

引用来自“eechen”的评论

PHP跟Apache和MySQL都诞生于1995年.2004年的时候PHP已经是年度编程语言.2015年底发布的高性能PHP7又给PHP注入了新的活力.Go有Google这座靠山,又有原贝尔实验室的大牛Rob Pike和Ken Thompson坐阵,有这么雄厚的资源,四五年的时间可不算短了.对比看看PHP之父Rasmus用Perl和C搞自己的主页,再慢慢开发PHP,真可谓一穷二白呀.
PHP于1994年由Rasmus Lerdorf创建,刚刚开始是Rasmus Lerdorf为了要维护个人网页而制作的一个简单的用Perl语言编写的程序。这些工具程序用来显示 Rasmus Lerdorf 的个人履历,以及统计网页流量。
真是神TM之逻辑.这也能和PHP扯上关系?

引用来自“eechen”的评论

还是PHP跟C的亲和性好,执行 php-src/ext/ext_skel 就可以生成PECL C扩展源码骨架,php-src/ext/下还有各种现有扩展作为例子,会点C语言的都可以用C给PHP写API了.http://wiki.swoole.com/wiki/page/238.html
实在忍不住要喷了. 现代语言基本都有FFI(Foreign Function Interface)支持. Rust和Go是作为语言一部分. Lua、 Ruby、 Node的FFI模块. Python的ctypes、 cffi, 甚至还有更大杀器的Cython和Pyre. Java的JNI. 当这些语言要调用一个C编译成的动态链接库时只要做下声明或者引入部分头文件, 就可以直接开调了, 也就两三行的事, 而坑爹的PHP呢? 要先找php源码包里的skel生成一个中间扩展, 再把扩展填了, 接着 phpize ./configure make 接下来make install再修改php.ini文件, 或者php里dl调用 就这傻逼到家的交互性也有脸自称和C亲和性好?
能不用CGO尽量不用吧。。。。。
@a3509007 ext_skel生成扩展骨架,phpize生成configure,然后就是传统的三部曲configure/make/make install,多么的C风格呀,还不服.反正PHP内置了SQLite,不服赶紧用你说的两三行代码来演示下Node和Java是如何使用SQLite这个共享库的嘛,让大家开开眼界呀,少装逼才好呢.

引用来自“eechen”的评论

@a3509007 ext_skel生成扩展骨架,phpize生成configure,然后就是传统的三部曲configure/make/make install,多么的C风格呀,还不服.反正PHP内置了SQLite,不服赶紧用你说的两三行代码来演示下Node和Java是如何使用SQLite这个共享库的嘛,让大家开开眼界呀,少装逼才好呢.
拿标准库打脸的话, php不装pecl里的pthread支持一下多线程看看? 要求再低点, "跨平台"的php在windows下面跑一下标准库里的pcntl看看? 一个太监语言长两个菊花一样是太监啊. 再退一千步, python的标准库里可是有sqlite的. 我就搞不懂了, 主流语言直接通过FFI机制调用链接库里的函数只需要本语言写两行就够了, 大PHP想实现一个FFI还特么在十多年前就太监了, 搞得现在还得通过几个世纪前的生成中间bridge封装来调, 弱鸡成这样还有脸吹和C亲和性好? C丢不起这人, 求你别拿它来拉近乎.
再往回说, 支持ffi的主流语言们调用个shared library连个GCC都不用装, 大php不仅要通过php源码包生成bridge, 还特么gcc、 automake、 autoconf、 make一个都不能少, 先天残障老要吹成天才有意思么......
@a3509007 PHP对多线程支持不知道多少年了像Apache Worker/Event MPM和Apache WinNT MPM这些架构上的多线程支持,都不知道多少年了,pthreads这个PECL扩展只不过是PHP编码上的支持,而且也只适合在CLI下用pthreads扩展,PHP7都不允许在PHP-FPM和MOD_PHP上运行pthreads了,或者是pthreads PECL作者故意为之,因为架构上的PHP的多线程实现并不需要PHP过分关心,从来不会有人说PHP-FPM和MOD_PHP不能利用多核,这是PHP一贯的架构和运行模式决定的,PHP开发者根本不需要费心去考虑这些,所以pthreads是永远不可能进入PHP代码主干的,就算pthreads作者的另一个扩展phpdbg已经进入PHP7主干.

引用来自“eechen”的评论

@a3509007 PHP对多线程支持不知道多少年了像Apache Worker/Event MPM和Apache WinNT MPM这些架构上的多线程支持,都不知道多少年了,pthreads这个PECL扩展只不过是PHP编码上的支持,而且也只适合在CLI下用pthreads扩展,PHP7都不允许在PHP-FPM和MOD_PHP上运行pthreads了,或者是pthreads PECL作者故意为之,因为架构上的PHP的多线程实现并不需要PHP过分关心,从来不会有人说PHP-FPM和MOD_PHP不能利用多核,这是PHP一贯的架构和运行模式决定的,PHP开发者根本不需要费心去考虑这些,所以pthreads是永远不可能进入PHP代码主干的,就算pthreads作者的另一个扩展phpdbg已经进入PHP7主干.
标准库不支持就不支持呗, 扯什么犊子, 照这种能被嵌入其他模型容器进程里抽调多线程也算支持多线程的话, 语言层仅仅支持协程的lua是不是也算能支持多线程了? 你要说pecl支持也算支持的话,那除了python自带sqlite, 其他语言没一个缺sqlite绑定的, 诡辩有意思么? 再回到争论的原点, 再问你一次, 其他主流语言本语言依靠ffi机制写两三行就能抽调so/dll里暴露出的函数, PHP里又要找php源码包, 又要装编译套件, 又要生成中间扩展, 又要编译, 你大PHP有脸说自己和C亲和性好? 而且和C亲和性好还体现在被C/C++调用吧, 你大PHP偷偷摸摸在sapi目录下加入embeded是哪个版本后的事? 还别说官方文档都找不到一页, 更别提还没见人用, ruby在被RPG maker嵌入当成用户控制语言都快20年了......你大PHP堪称主流语言里和C交互性最烂的, 而且很大概率还没有"之一".
@a3509007 你说了那么多废话,还是没有把你口中所说的"几句代码"就能调用C库的Node和Java示例写出来给大家见识下,有意思么?看看人家鸟哥和峰哥这些C大神和PHP大神的作品,再看看你在这里黑吹,这就是差距呀.看着PHP内置的ext下的C扩展和第三方PECL C库,还在这里装13,人不要脸,还真以为天下无敌呀,少在我面前秀逗.

引用来自“eechen”的评论

@a3509007 你说了那么多废话,还是没有把你口中所说的"几句代码"就能调用C库的Node和Java示例写出来给大家见识下,有意思么?看看人家鸟哥和峰哥这些C大神和PHP大神的作品,再看看你在这里黑吹,这就是差距呀.看着PHP内置的ext下的C扩展和第三方PECL C库,还在这里装13,人不要脸,还真以为天下无敌呀,少在我面前秀逗.
你搜"java jni" "lua/ruby/node FFI" "python ctypes/cffi/cython"都能搜到一堆例子, 需要我来在OSC这个连换行都不支持的留言区贴给你么? 转换角度的说, 我了解php怎么写扩展, 我也了解其他语言怎么调用动态连接库, 你不了解是你无能, 我凭什么贴给你? 拿虎皮作大旗很威风么? laruence是很牛啊, 问题是关我毛事, 又关你毛事? laruence很牛php在和C交互方面就不先天残障了么?
@a3509007 几行代码都不愿意贴出来,你还装什么装,就凭你这种眼高手低的装逼犯,还想黑PHP的C扩展能力,真是不要脸.

引用来自“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等等, 那我道歉好了......

引用来自“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的未来
看来PHP脑残粉连老祖宗语言C都敢撕,我Java神教都要让C三分啊。。。。
顶部