1. 程式人生 > >CGO_ENABLED環境變數對Go靜態編譯機制的影響

CGO_ENABLED環境變數對Go靜態編譯機制的影響

Go有很多優點,比如:簡單、原生支援併發等,而不錯的可移植性也是Go被廣大程式設計師接納的重要因素之一。但你知道為什麼Go語言擁有很好的平臺可移植性嗎?本著“知其然,亦要知其所以然”的精神,本文我們就來探究一下Go良好可移植性背後的原理。

一、Go的可移植性

說到一門程式語言可移植性,我們一般從下面兩個方面考量:

  • 語言自身被移植到不同平臺的容易程度;
  • 通過這種語言編譯出來的應用程式對平臺的適應性。

在Go 1.7及以後版本中,我們可以通過下面命令檢視Go支援OS和平臺列表:

123456789101112131415161718192021222324252627282930313233343536373839 $gotooldistlistandroid/386android/amd64android/armandroid/arm64darwin/386darwin/amd64darwin/armdarwin/arm64dragonfly/amd64freebsd/386freebsd/amd64freebsd/armlinux/386linux/amd64linux/armlinux/arm64linux/mipslinux/
mips64linux/mips64lelinux/mipslelinux/ppc64linux/ppc64lelinux/s390xnacl/386nacl/amd64p32nacl/armnetbsd/386netbsd/amd64netbsd/armopenbsd/386openbsd/amd64openbsd/armplan9/386plan9/amd64plan9/armsolaris/amd64windows/386windows/amd64

從上述列表我們可以看出:從linux/arm64的嵌入式系統到linux/s390x的大型機系統,再到Windows、linux和darwin(mac)這樣的主流作業系統、amd64、386這樣的主流處理器體系,Go對各種平臺和作業系統的支援不可謂不廣泛。

Go官方似乎沒有給出明確的porting guide,關於將Go語言porting到其他平臺上的內容更多是在golang-dev這樣的小圈子中討論的事情。但就Go語言這麼短的時間就能很好的支援這麼多平臺來看,Go的porting還是相對easy的。從個人對Go的瞭解來看,這一定程度上得益於Go獨立實現了runtime。

img{512x368}

runtime是支撐程式執行的基礎。我們最熟悉的莫過於libc(C執行時),它是目前主流作業系統上應用最普遍的執行時,通常以動態連結庫的形式(比如:/lib/x86_64-linux-gnu/libc.so.6)隨著系統一併釋出,它的功能大致有如下幾個:

  • 提供基礎庫函式呼叫,比如:strncpy;
  • 封裝syscall(注:syscall是作業系統提供的API口,當用戶層進行系統呼叫時,程式碼會trap(陷入)到核心層面執行),並提供同語言的庫函式呼叫,比如:malloc、fread等;
  • 提供程式啟動入口函式,比如:linux下的__libc_start_main。

libc等c runtime lib是很早以前就已經實現的了,甚至有些老舊的libc還是單執行緒的。一些從事c/c 開發多年的程式設計師早年估計都有過這樣的經歷:那就是連結runtime庫時甚至需要選擇連結支援多執行緒的庫還是隻支援單執行緒的庫。除此之外,c runtime的版本也參差不齊。這樣的c runtime狀況完全不能滿足go語言自身的需求;另外Go的目標之一是原生支援併發,並使用goroutine模型,c runtime對此是無能為力的,因為c runtime本身是基於執行緒模型的。綜合以上因素,Go自己實現了runtime,並封裝了syscall,為不同平臺上的go user level程式碼提供封裝完成的、統一的go標準庫;同時Go runtime實現了對goroutine模型的支援。

獨立實現的go runtime層將Go user-level code與OS syscall解耦,把Go porting到一個新平臺時,將runtime與新平臺的syscall對接即可(當然porting工作不僅僅只有這些);同時,runtime層的實現基本擺脫了Go程式對libc的依賴,這樣靜態編譯的Go程式具有很好的平臺適應性。比如:一個compiled for linux amd64的Go程式可以很好的運行於不同linux發行版(centos、ubuntu)下。

以下測試試驗環境為:darwin amd64 Go 1.8。

二、預設”靜態連結”的Go程式

我們先來寫兩個程式:hello.c和hello.go,它們完成的功能都差不多,在stdout上輸出一行文字:

12345678910111213141516 //hello.c#include intmain(){printf("%s\n","hello, portable c!");return0;}//hello.gopackagemainimport"fmt"funcmain(){fmt.Println("hello, portable go!")}

我們採用“預設”方式分別編譯以下兩個程式:

123456 $cc-ohellochello.c$gobuild-ohellogohello.go$ls-l-rwxr-xr-x1tonystaff849662714:18helloc*-rwxr-xr-x1tonystaff162819262714:18hellogo*

從編譯後的兩個檔案helloc和hellogo的size上我們可以看到hellogo相比於helloc簡直就是“巨人”般的存在,其size近helloc的200倍。略微學過一些Go的人都知道,這是因為hellogo中包含了必需的go runtime。我們通過otool工具(linux上可以用ldd)檢視一下兩個檔案的對外部動態庫的依賴情況:

12345 $otool-Lhellochelloc:/usr/lib/libSystem.B.dylib(compatibilityversion1.0.0,currentversion1197.1.1)$otool-Lhellogohellogo:

通過otool輸出,我們可以看到hellogo並不依賴任何外部庫,我們將hellog這個二進位制檔案copy到任何一個mac amd64的平臺上,均可以執行起來。而helloc則依賴外部的動態庫:/usr/lib/libSystem.B.dylib,而libSystem.B.dylib這個動態庫還有其他依賴。我們通過nm工具可以檢視到helloc具體是哪個函式符號需要由外部動態庫提供:

12345 $nmhelloc0000000100000000T__mh_execute_header0000000100000f30T_mainU_printfUdyld_stub_binder

可以看到:_printf和dyld_stub_binder兩個符號是未定義的(對應的字首符號是U)。如果對hellog使用nm,你會看到大量符號輸出,但沒有未定義的符號。

1234567891011121314151617181920212223242526272829303132333435363738 $nmhellogo00000000010bb278s$f64.3eb000000000000000000000010bb280s$f64.3fd000000000000000000000010bb288s$f64.3fe000000000000000000000010bb290s$f64.3fee66666666666600000000010bb298s$f64.3ff000000000000000000000010bb2a0s$f64.401400000000000000000000010bb2a8s$f64.402400000000000000000000010bb2b0s$f64.403a00000000000000000000010bb2b8s$f64.405900000000000000000000010bb2c0s$f64.43e000000000000000000000010bb2c8s$f64.800000000000000000000000010bb2d0s$f64.bfe62e42fefa39ef000000000110af40b__cgo_init000000000110af48b__cgo_notify_runtime_init_done000000000110af50b__cgo_thread_start000000000104d1e0t__rt0_amd64_darwin000000000104a0f0t_callRet000000000104b580t_gosave000000000104d200T_main00000000010bbb20s_masks000000000104d370t_nanotime000000000104b7a0t_setg_gcc00000000010bbc20s_shifts0000000001051840terrors.(*errorString).Error00000000010517a0terrors.New.......0000000001065160ttype..hash.time.Time0000000001064f70ttype..hash.time.zone00000000010650a0ttype..hash.time.zoneTrans0000000001051860tunicode/utf8.DecodeRuneInString0000000001051a80tunicode/utf8.EncodeRune0000000001051bd0tunicode/utf8.RuneCount0000000001051d10tunicode/utf8.RuneCountInString0000000001107080sunicode/utf8.acceptRanges00000000011079e0sunicode/utf8.first$nmhellogo|grep" U "

Go將所有執行需要的函式程式碼都放到了hellogo中,這就是所謂的“靜態連結”。是不是所有情況下,Go都不會依賴外部動態共享庫呢?我們來看看下面這段程式碼:

123456789101112131415161718192021 //server.gopackagemainimport("log""net/http""os")funcmain(){cwd,err:=os.Getwd()iferr!=nil{log.Fatal(err)}srv:=&http.Server{Addr:":8000",// Normally ":443"Handler:http.FileServer(http.Dir(cwd)),}log.Fatal(srv.ListenAndServe())}

我們利用Go標準庫的net/http包寫了一個fileserver,我們build一下該server,並檢視它是否有外部依賴以及未定義的符號:

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162 $gobuildserver.go-rwxr-xr-x1tonystaff594382862714:47server*$otool-Lserverserver:/usr/lib/libSystem.B.dylib(compatibilityversion0.0.0,currentversion0.0.0)/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation(compatibilityversion0.0.0,currentversion0.0.0)/System/Library/Frameworks/Security.framework/Versions/A/Security(compatibilityversion0.0.0,currentversion0.0.0)/usr/lib/libSystem.B.dylib(compatibilityversion0.0.0,currentversion0.0.0)/usr/lib/libSystem.B.dylib(compatibilityversion0.0.0,currentversion0.0.0)$nmserver|grep" U "U_CFArrayGetCountU_CFArrayGetValueAtIndexU_CFDataAppendBytesU_CFDataCreateMutableU_CFDataGetBytePtrU_CFDataGetLengthU_CFDictionaryGetValueIfPresentU_CFEqualU_CFNumberGetValueU_CFReleaseU_CFStringCreateWithCStringU_SecCertificateCopyNormalizedIssuerContentU_SecCertificateCopyNormalizedSubjectContentU_SecKeychainItemExportU_SecTrustCopyAnchorCertificatesU_SecTrustSettingsCopyCertificatesU_SecTrustSettingsCopyTrustSettingsU___errorU___stack_chk_failU___stack_chk_guardU___stderrpU_abortU_fprintfU_fputcU_freeU_freeaddrinfoU_fwriteU_gai_strerrorU_getaddrinfoU_getnameinfoU_kCFAllocatorDefaultU_mallocU_memcmpU_nanosleepU_pthread_attr_destroy

相關推薦

CGO_ENABLED環境變數Go靜態編譯機制影響

Go有很多優點,比如:簡單、原生支援併發等,而不錯的可移植性也是Go被廣大程式設計師接納的重要因素之一。但你知道為什麼Go語言擁有很好的平臺可移植性嗎?本著“知其然,亦要知其所以然”的精神,本文我們就來探究一下Go良好可移植性背後的原理。 一、Go的可移植性 說到一門程式語言可移植性,我們一般從下面兩

hp機器的PLATFORM環境變數RAD XE的編譯、DevExpress Vcl安裝的衝突問題解決

hp手提電腦,用安裝助手DXAutoInstaller 安裝DEVEXPRESS VCL出現下圖這個情況,Rad XE在編譯時也出現Platform不能識別的問題! 折騰了好久,終於找到解決辦法了! 

linux新增環境變數/etc/profile檔案進行修改過後導致命令用不了

在使用java的時候,要新增環境變數。一般我們是export一些變數,比如: export JAVA_HOME=.... export PATH=$PATH:$JAVA_HOME/bin 但是,當我們在export PATH的時候忘記在等號右邊加紅色的$PATH:

NDK環境變數的配置以及編譯方法(linux環境下)

1、下載NDK 2 、配置NDK的環境變數 a 、sudo gedit .bashrc (這裡的bashrc檔案在不同電腦下所在的路徑不同) b 、export PATH=$PATH:/work/

使用CMD命令建立臨時Java環境變數Maven環境變數,使用maven編譯專案

1、找到專案所在目錄,   //cd D:   ;2、切換目錄: cd  IdeaProject ,cd code(專案根目錄);3、使用echo ”%JAVA_HOME%“檢視預設Java環境變數配置

Halide入門第3講:如何設定環境變數以檢查llvm編譯生成的程式碼

// Halide tutorial lesson 3: Inspecting the generated code // Halide入門第3講:檢查llvm編譯生成的程式碼 // This lesson demonstrates how to inspect what the Halide

GCC 編譯使用動態連結庫和靜態連結庫--及先後順序----及環境變數設定總結

1 庫的分類 根據連結時期的不同,庫又有靜態庫和動態庫之分。 靜態庫是在連結階段被連結的(好像是廢話,但事實就是這樣),所以生成的可執行檔案就不受庫的影響了,即使庫被刪除了,程式依然可以成功執行。 有別於靜態庫,動態庫的連結是在程式執行的時候被連結的。所以,即使

xmake新增WDK驅動編譯環境支持

說明 down 插件 level x86 可讀性 ocs 不能 根據 xmake v2.2.1新版本現已支持WDK驅動編譯環境,我們可以直接在系統原生cmd終端下,執行xmake進行驅動編譯,甚至配合vscode, sublime text, IDEA等編輯器+xmake插

Go 語言執行時環境變數快速導覽

原文: http://dave.cheney.net/2015/11/29/a-whirlwind-tour-of-gos-runtime-environment-variables Go 語言執行時環境變數快速導覽 Go Runtime除了提供:GC, goroutine排程, 定時器,

環境變數HISTCONTROL命令及快捷鍵Ctrl+o命令的影響

在linux中環境變數HISTCONTROL可以控制歷史的記錄方式。  HISTCONTROL有以下的選項: ignoredups          預設,忽略重複命令 ignorespace       

go 環境變數配置

1、下載壓縮包:go1.8.3.linux-amd64.tar.gz 2、 vi /etc/profile 3、在配置檔案後面新增內容: export GOROOT=/usr/local/go export GOPATH=/usr/local/go/bin GOPATH 放

cl編譯C檔案的環境變數修改

添 加環境 變數INCLUDEC:\Program Files (x86)\Windows Kits\10\Include\10.0.10240.0\ucrt;C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include LIBC:\Progr

GO語言學習:安裝包下載和linux環境變數配置

1.安裝包下載   地址:https://golang.google.cn/dl/   截圖:    2.解壓安裝包   解壓後的檔案如下圖:     把解壓後的檔案放在 、usr/local/下,如下圖      3.配置環境變數   使用root許可權登入

Go安裝之linux下如何配置Go語言環境變數

文章目錄 1. 登入root 使用者,修改配置 2. 修改內容 3. 生效配置,並測試是否可用 1. 登入root 使用者,修改配置 切換到root使用者模式 $ sudo -i password: 開啟/etc/p

go的解除安裝和環境變數配個人.bashrc

若是用安裝包直接解壓 http://download.csdn.net/detail/u010026901/7592581   cd /usr/local tar -zxvf go1.1.2.linux-386.tar.gz(先把安裝包移到這個目錄) &nbs

Linux普通使用者java環境變數進行配置

具體操作如下: 1、下載jdk安裝包到伺服器 2、解壓 tar -zxvf jdk-8u131-linux-x64.tar.gz3、配置環境變數    3.1、vi ~/.bash_profile    3.2、在.bash_profile檔案中填入如下語句:     export PATH    expor

Windows 平臺下 Go 語言的安裝和環境變數設定

1. Go 語言 SDK 安裝包下載和安裝 最新穩定版 1.5.3 安裝包 go1.5.3.windows-amd64.msi下載地址 https://golang.org/dl/,大小約 69 MB(作者上傳了一份該版本安裝包到 CSDN 資源,嫌國外網速慢可以去下載:h

Go語言常見混淆問題03--mac環境go環境變數的配置

1.導言 在學習go語言的時候,如果是windows系統那麼配置系統環境變數就特別容易。直接可以在我的電腦-屬性-高階裡面直接圖形化介面設定就可以。但是在mac環境下也就是linux環境下go的環境變數如何配置就顯得有點蛋疼。畢竟咱mac環境不存在我的電腦這玩意。 2.

以太坊(go-ethereum)編譯除錯環境搭建

以下步驟都是在MacOs上操作的,但同樣適合Ubuntu,只是有幾個小點有所不同,我會標註出來編譯1. Go環境搭建    Mac: (brew是一個類似ubuntu apt-get的工具,用來在終端安裝軟體的)brew update && brew upgr

Go基本安裝及環境變數說明

為方便開發,在開發環境的安裝中需要注意的是個三個環境變數的設定: 1、$GOROOT:go的安裝目錄,配置後不會再更改; 2、$PATH:需要將go的bin目錄新增到系統$PATH中以便方便使用go的相關命令,配置後也不會再更改; 3、$GOPATH:go專案在本地的開發環境的的專案根路徑(以便專案編