自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

在 Go 里用 CGO?這 7 個(gè)問題你要關(guān)注!

開發(fā) 前端
在 Go 項(xiàng)目中被過度使用了,當(dāng)面臨在 Go 中重新實(shí)現(xiàn)一大段 C 語言代碼時(shí),程序員會(huì)選擇使用 cgo 來包裝庫,認(rèn)為這是個(gè)更容易解決的問題。但我認(rèn)為這是一種錯(cuò)誤的選擇行為。

大家好,我是煎魚。

今天給大家分享的是 Go 諺語中的 Cgo is not Go[1],原文章同名,略有修改,原文作者是 @Dave Cheney。以下的 “我” 均指代原作者。

借用 JWZ 的一句話:有些人在面對(duì)一個(gè)問題時(shí),認(rèn)為 "我知道,我會(huì)使用 cgo(來解決這個(gè)問題)"。

圖片

類似的引言

在使用 cgo 后,他們就會(huì)遇到兩個(gè)新問題。

Cgo 是什么

Cgo 是一項(xiàng)了不起的技術(shù),它允許 Go 程序與 C 語言庫相互操作,這是一個(gè)非常有用的功能。

沒有它,Go 就不會(huì)有今天的地位。cgo 是在 Android 和 iOS 上運(yùn)行 Go 程序的關(guān)鍵。

注:甚至許多內(nèi)部用到其他底層語言的同學(xué),會(huì)使用它來做膠水。

被過度使用

我個(gè)人認(rèn)為 cgo 在 Go 項(xiàng)目中被過度使用了,當(dāng)面臨在 Go 中重新實(shí)現(xiàn)一大段 C 語言代碼時(shí),程序員會(huì)選擇使用 cgo 來包裝庫,認(rèn)為這是個(gè)更容易解決的問題。但我認(rèn)為這是一種錯(cuò)誤的選擇行為。

顯然,在某些情況下,cgo 是不可避免的,最明顯的是你必須與圖形驅(qū)動(dòng)或窗口系統(tǒng)進(jìn)行互操作,而后者只能以二進(jìn)制 blob 的形式提供。在這些場(chǎng)景下,cgo 的使用證明了它的權(quán)衡是合理的,比許多人準(zhǔn)備承認(rèn)的要少得多。

以下是一份不完整的權(quán)衡清單,當(dāng)你把 Go 項(xiàng)目建立在 cgo 庫上時(shí),你可能沒有意識(shí)到這些權(quán)衡。

你需要對(duì)此進(jìn)行思考。

構(gòu)建時(shí)間變長(zhǎng)

當(dāng)你在 Go 包中導(dǎo)入 "C" 時(shí),go build 需要做更多的工作來構(gòu)建你的代碼。

構(gòu)建你的包不再是簡(jiǎn)單地將范圍內(nèi)的所有 .go 文件的列表傳遞給 go 工具編譯的一次調(diào)用,而是包含以下工作項(xiàng):

  • 需要調(diào)用 cgo 工具來生成 C 到 Go 和 Go 到 C 的相關(guān)代碼。
  • 系統(tǒng)中的 C 編譯器會(huì)為軟件包中的每個(gè) C 文件進(jìn)行調(diào)用處理。
  • 各個(gè)編譯單元被合并到一個(gè) .o 文件中。
  • 生成的 .o 文件會(huì)通過系統(tǒng)的鏈接器,對(duì)其引用的共享對(duì)象進(jìn)行修正。

所有這些工作在你每次編譯或測(cè)試你的軟件包時(shí)都會(huì)發(fā)生,如果你在該軟件包中積極工作的話,這種情況是經(jīng)常發(fā)生的。

Go 工具會(huì)在可能的情況下將這些工作并行化(包括對(duì)所有的 C 代碼進(jìn)行全面重建),軟件包的編譯時(shí)間將會(huì)增加,并會(huì)隨之增大而增大。

你還需要在各大平臺(tái)上調(diào)試你的 C 語言代碼,以避免由于兼容性導(dǎo)致的編譯失敗。

復(fù)雜的構(gòu)建

Go 的目標(biāo)之一是產(chǎn)生一種語言,它的構(gòu)建過程是自我描述的;你的程序的源代碼包含了足夠的信息,可以讓一個(gè)工具來構(gòu)建這個(gè)項(xiàng)目。這并不是說使用 Makefile 來自動(dòng)構(gòu)建工作流程是不好的,但是在 cgo 被引入項(xiàng)目之前,除了 go 工具之外,你可能不需要任何東西來構(gòu)建和測(cè)試。

在引入了 cgo 之后,你需要設(shè)置所有的環(huán)境變量,跟蹤可能安裝在奇怪地方的共享對(duì)象和頭文件。

另外需要注意,Go 支持許多的平臺(tái),而 cgo 并不是。所以你必須花一些時(shí)間來為你的 Windows 用戶想出一個(gè)解決方案。

現(xiàn)在你的用戶必須安裝 C 編譯器,而不僅僅是 Go 編譯器。他們還必須安裝你的項(xiàng)目所依賴的 C 語言庫,你也要承擔(dān)這個(gè)技術(shù)支持的成本。

交叉匯編被拋在窗外

Go 對(duì)交叉編譯的支持是同類中最好的。從 Go 1.5 開始,你可以通過 Go 項(xiàng)目網(wǎng)站上的官方安裝程序支持從任何平臺(tái)交叉編譯到任何其他平臺(tái)。

在默認(rèn)情況下,交叉編譯時(shí) cgo 被禁用。通常情況下,如果你的項(xiàng)目是純粹的 Go,這不是一個(gè)問題。

當(dāng)你混入對(duì) C 庫的依賴時(shí),你要么放棄交叉編譯你的因那個(gè)也,要么你必須投入時(shí)間為所有目標(biāo)尋找和維護(hù)交叉編譯的 C 工具鏈,才能實(shí)現(xiàn)交叉編譯。

Go 支持的平臺(tái)數(shù)量在不斷增加。Go 1.5 增加了對(duì) 64 位 ARM 和 PowerPC 的支持。Go 1.6 增加了對(duì) 64 位 MIPS 的支持,而 IBM 的 s390 架構(gòu)被吹捧為 Go 1.7。RISC-V 正在開發(fā)中。

如果你的產(chǎn)品依賴于 C 語言庫,你不僅有上述交叉編譯的所有問題,你還必須確保你所依賴的 C 語言代碼在 Go 支持的新平臺(tái)上可靠地工作 -- 而且你必須在 C/Go 混合語言為你提供的有限調(diào)試能力的情況下做到這一點(diǎn)。

你失去了對(duì)所有工具的訪問權(quán)

Go 有很好的工具;我們有 race detector、用于分析代碼的 pprof、覆蓋率、模糊測(cè)試和源代碼分析工具。但這些工具都不能在 cgo 中起到作用(也就是沒法排查)。

相反,像 valgrind 這樣優(yōu)秀的工具并不了解 Go 的調(diào)用約定或堆棧布局。在這一點(diǎn)上,Ian Lance Taylor 的工作是整合 clang 的內(nèi)存凈化器來調(diào)試 C 端的懸空指針,這對(duì) Go 1.6 中的 cgo 用戶有好處。

將 Go 代碼和 C 代碼結(jié)合起來的結(jié)果是兩個(gè)世界的交叉點(diǎn),而不是結(jié)合點(diǎn);C 的內(nèi)存安全和 Go 程序的調(diào)試性。但失去了許多核心工具的使用空間。

性能將始終是一個(gè)問題

C 代碼和 Go 代碼生活在兩個(gè)不同的世界里,cgo 穿越了它們之間的邊界,這種轉(zhuǎn)換不是免費(fèi)的。而且取決于它在你的代碼中存在的位置,其成本可能是無關(guān)緊要的,也可能是巨大的。

?C 對(duì) Go 的調(diào)用慣例或可增長(zhǎng)的堆棧一無所知,所以對(duì) C 代碼的調(diào)用必須記錄 goroutine 堆棧的所有細(xì)節(jié),切換到 C 堆棧,并運(yùn)行 C 代碼,而 C 代碼對(duì)它是如何被調(diào)用的,或負(fù)責(zé)程序的更大的 Go 運(yùn)行時(shí)一無所知。

公平地說,Go 對(duì) C 的世界也一無所知。這就是為什么隨著時(shí)間的推移,兩者之間的數(shù)據(jù)傳遞規(guī)則變得越來越繁瑣,因?yàn)榫幾g器越來越善于發(fā)現(xiàn)不再被認(rèn)為是有效的堆棧數(shù)據(jù),而垃圾回收器也越來越善于對(duì)堆進(jìn)行同樣的處理。

如果在 C 語言世界中出現(xiàn)故障,Go 代碼必須恢復(fù)足夠的狀態(tài),至少要打印出堆棧跟蹤并干凈地退出程序,而不是把核心文件的信息都暴露出來。

管理這種跨調(diào)用堆棧的過渡,尤其是涉及到信號(hào)、線程和回調(diào)的情況下,是不容易的(Ian Lance Taylor 在 Go 1.6 中也做了大量的工作來改善信號(hào)處理與 C 的互操作性)。

歸根結(jié)底,C 語言和 Go 語言之間的轉(zhuǎn)換是不容易的,互相對(duì)對(duì)方都一戶無知,會(huì)有明顯的性能開銷。

C 語言發(fā)號(hào)施令,而不是你的代碼

你用哪種語言編寫綁定或包裝 C 代碼并不重要;Python、使用 JNI 的 Java、使用 libFFI 的一些語言,或者通過 cgo 的 Go;這是 C 的世界,你只是生活在其中。

Go 代碼和 C 代碼必須就如何共享地址空間、信號(hào)處理程序和線程 TLS 槽等資源達(dá)成一致 -- 我說的一致,實(shí)際上是指 Go 必須圍繞 C 代碼的假設(shè)開展工作。C 代碼可以假設(shè)它總是在一個(gè)線程上運(yùn)行,或者根本沒有準(zhǔn)備好在多線程環(huán)境下工作。

你不是在寫一個(gè)使用 C 庫的邏輯的 Go 程序,是在寫一個(gè)必須與互不可控的 C 代碼共存的 Go 程序,這個(gè) C 代碼很難被取代,在談判中占上風(fēng),而且不關(guān)心你的問題。

部署變得更加復(fù)雜

任何對(duì)普通觀眾的 Go 演講都會(huì)包含至少一張帶有這些文字的幻燈片:Single, static binary(單一的、靜態(tài)的二進(jìn)制)。

這是 Go 的一張王牌,使其成為遠(yuǎn)離虛擬機(jī)和運(yùn)行時(shí)管理的典型代表。使用 cgo,你就放棄了這一點(diǎn),放棄了 Go 的優(yōu)勢(shì)區(qū)域。

根據(jù)你的環(huán)境,你可能會(huì)把你的 Go 項(xiàng)目編譯成 deb 或 rpm,并且假設(shè)你的其他依賴項(xiàng)也被打包了,把它們作為安裝依賴項(xiàng)加入,把問題推給操作系統(tǒng)的軟件包管理器。但這對(duì)以前像 go build && scp 那樣直接的構(gòu)建和部署過程來說,是有幾個(gè)重大的變化。

完全靜態(tài)地編譯 Go 程序是可能的,但這絕不是簡(jiǎn)單的,這表明在項(xiàng)目中加入 cgo 的影響會(huì)波及整個(gè)構(gòu)建和部署的生命周期。

明智的選擇

說白了,我并不是說你不應(yīng)該使用 cgo。但是在你做這個(gè)設(shè)計(jì)前,請(qǐng)仔細(xì)考慮你將會(huì)放棄的 Go 的許多品質(zhì)。

需要考慮清楚得失,再思考是否值得你這么去做。

參考資料

[1]Cgo is not Go: https://dave.cheney.net/2016/01/18/cgo-is-not-go???

責(zé)任編輯:武曉燕 來源: 腦子進(jìn)煎魚了
相關(guān)推薦

2023-04-26 08:43:28

GoCGO語言

2019-06-14 10:56:43

JavaMaven編程語言

2021-04-23 07:59:17

Godefer 鏈表

2015-09-08 10:22:42

2020-08-06 08:27:21

JavaScript概念語言

2019-07-25 13:51:01

攜號(hào)轉(zhuǎn)網(wǎng)工信部聞庫

2009-03-17 08:31:08

Window 7微軟用戶

2021-05-07 06:22:51

XDR端點(diǎn)安全安全運(yùn)營(yíng)

2020-02-05 14:42:52

網(wǎng)絡(luò)安全IT安全漏洞

2021-12-28 10:25:04

數(shù)據(jù)庫SQL技術(shù)

2017-05-17 17:23:00

2021-04-27 10:14:33

Go業(yè)務(wù)函數(shù)

2021-06-10 07:59:40

Linux 系統(tǒng)硬件操作系統(tǒng)

2019-10-16 11:12:41

云安全云計(jì)算數(shù)據(jù)

2020-06-29 19:15:33

5G頻譜運(yùn)營(yíng)商

2022-03-09 17:37:55

前端架構(gòu)微前端

2021-01-10 23:36:52

SQL數(shù)據(jù)庫技術(shù)

2019-10-18 15:16:10

Redis數(shù)據(jù)庫并發(fā)

2019-04-11 13:25:58

2022-12-07 09:00:18

錯(cuò)誤異常CGO
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)