真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

go語(yǔ)言靜態(tài)文件 go靜態(tài)編譯

如何在golang 中調(diào)用c的靜態(tài)庫(kù)或者動(dòng)態(tài)庫(kù)

Cgo 使得Go程序能夠調(diào)用C代碼. cgo讀入一個(gè)用特別的格式寫的Go語(yǔ)言源文件, 輸出Go和C程序, 使得C程序能打包到Go語(yǔ)言的程序包中.

成都一家集口碑和實(shí)力的網(wǎng)站建設(shè)服務(wù)商,擁有專業(yè)的企業(yè)建站團(tuán)隊(duì)和靠譜的建站技術(shù),十多年企業(yè)及個(gè)人網(wǎng)站建設(shè)經(jīng)驗(yàn) ,為成都數(shù)千家客戶提供網(wǎng)頁(yè)設(shè)計(jì)制作,網(wǎng)站開發(fā),企業(yè)網(wǎng)站制作建設(shè)等服務(wù),包括成都營(yíng)銷型網(wǎng)站建設(shè),高端網(wǎng)站設(shè)計(jì),同時(shí)也為不同行業(yè)的客戶提供網(wǎng)站設(shè)計(jì)、網(wǎng)站制作的服務(wù),包括成都電商型網(wǎng)站制作建設(shè),裝修行業(yè)網(wǎng)站制作建設(shè),傳統(tǒng)機(jī)械行業(yè)網(wǎng)站建設(shè),傳統(tǒng)農(nóng)業(yè)行業(yè)網(wǎng)站制作建設(shè)。在成都做網(wǎng)站,選網(wǎng)站制作建設(shè)服務(wù)商就選成都創(chuàng)新互聯(lián)。

舉例說明一下. 下面是一個(gè)Go語(yǔ)言包, 包含了兩個(gè)函數(shù) -- Random 和 Seed -- 是C語(yǔ)言庫(kù)中random和srandom函數(shù)的馬甲.

package rand

/*

#include stdlib.h

*/ import "C" func Random() int { return int(C.random()) } func Seed(i int) { C.srandom(C.uint(i)) }

我們來看一下這里都有什么內(nèi)容. 開始是一個(gè)包的導(dǎo)入語(yǔ)句.

rand包導(dǎo)入了"C"包, 但你會(huì)發(fā)現(xiàn)在Go的標(biāo)準(zhǔn)庫(kù)里沒有這個(gè)包. 那是因?yàn)镃是一個(gè)"偽包", 一個(gè)為cgo引入的特殊的包名, 它是C命名空間的一個(gè)引用.

rand 包包含4個(gè)到C包的引用: 調(diào)用 C.random和C.srandom, 類型轉(zhuǎn)換 C.uint(i)還有引用語(yǔ)句.

Random函數(shù)調(diào)用libc中的random函數(shù), 然后回返結(jié)果. 在C中, random返回一個(gè)C類型的長(zhǎng)整形值, cgo把它輪換為C.long. 這個(gè)值必需轉(zhuǎn)換成Go的類型, 才能在Go程序中使用. 使用一個(gè)常見的Go類型轉(zhuǎn)換:

func Random() int { return int(C.random()) }

這是一個(gè)等價(jià)的函數(shù), 使用了一個(gè)臨時(shí)變量來進(jìn)行類型轉(zhuǎn)換:

func Random() int { var r C.long = C.random() return int(r) }

Seed函數(shù)則相反. 它接受一個(gè)Go語(yǔ)言的int類型, 轉(zhuǎn)換成C語(yǔ)言的unsigned int類型, 然后傳遞給C的srandom函數(shù).

func Seed(i int) { C.srandom(C.uint(i)) }

需要注意的是, cgo中的unsigned int類型寫為C.uint; cgo的文檔中有完整的類型列表.

這個(gè)例子中還有一個(gè)細(xì)節(jié)我們沒有說到, 那就是導(dǎo)入語(yǔ)句上面的注釋.

/*

#include stdlib.h

*/ import "C"

Cgo可以識(shí)別這個(gè)注釋, 并在編譯C語(yǔ)言程序的時(shí)候?qū)⑺?dāng)作一個(gè)頭文件來處理. 在這個(gè)例子中, 它只是一個(gè)include語(yǔ)句, 然而其實(shí)它可以是使用有效的C語(yǔ)言代碼. 這個(gè)注釋必需緊靠在import "C"這個(gè)語(yǔ)句的上面, 不能有空行, 就像是文檔注釋一樣.

Strings and things

與Go語(yǔ)言不同, C語(yǔ)言中沒有顯式的字符串類型. 字符串在C語(yǔ)言中是一個(gè)以0結(jié)尾的字符數(shù)組.

Go和C語(yǔ)言中的字符串轉(zhuǎn)換是通過C.CString, C.GoString,和C.GoStringN這些函數(shù)進(jìn)行的. 這些轉(zhuǎn)換將得到字符串類型的一個(gè)副本.

下一個(gè)例子是實(shí)現(xiàn)一個(gè)Print函數(shù), 它使用C標(biāo)準(zhǔn)庫(kù)中的fputs函數(shù)把一個(gè)字符串寫到標(biāo)準(zhǔn)輸出上:

package print // #include stdio.h // #include stdlib.h import "C" import "unsafe" func Print(s string) { cs := C.CString(s) C.fputs(cs, (*C.FILE)(C.stdout)) C.free(unsafe.Pointer(cs)) }

在C程序中進(jìn)行的內(nèi)存分配是不能被Go語(yǔ)言的內(nèi)存管理器感知的. 當(dāng)你使用C.CString創(chuàng)建一個(gè)C字符串時(shí)(或者其它類型的C語(yǔ)言內(nèi)存分配), 你必需記得在使用完后用C.free來釋放它.

調(diào)用C.CString將返回一個(gè)指向字符數(shù)組開始處的指錯(cuò), 所以在函數(shù)退出前我們把它轉(zhuǎn)換成一個(gè)unsafe.Pointer(Go中與C的void 等價(jià)的東西), 使用C.free來釋放分配的內(nèi)存. 一個(gè)慣用法是在分配內(nèi)存后緊跟一個(gè)defer(特別是當(dāng)這段代碼比較復(fù)雜的時(shí)候), 這樣我們就有了下面這個(gè)Print函數(shù):

func Print(s string) { cs := C.CString(s) defer C.free(unsafe.Pointer(cs)) C.fputs(cs, (*C.FILE)(C.stdout)) }

構(gòu)建 cgo 包

如果你使用goinstall, 構(gòu)建cgo包就比較容易了, 只要調(diào)用像平常一樣使用goinstall命令, 它就能自動(dòng)識(shí)別這個(gè)特殊的import "C", 然后自動(dòng)使用cgo來編譯這些文件.

如果你想使用Go的Makefiles來構(gòu)建, 那在CGOFILES變量中列出那些要用cgo處理的文件, 就像GOFILES變量包含一般的Go源文件一樣.

rand包的Makefile可以寫成下面這樣:

include $(GOROOT)/src/Make.inc

TARG=goblog/rand

CGOFILES=\ rand.go\ include $(GOROOT)/src/Make.pkg

然后輸入gomake開始構(gòu)建.

更多 cgo 的資源

cgo的文檔中包含了關(guān)于C偽包的更多詳細(xì)的說明, 以及構(gòu)建過程. Go代碼樹中的cgo的例子給出了更多更高級(jí)的用法.

一個(gè)簡(jiǎn)單而又符合Go慣用法的基于cgo的包是Russ Cox寫的gosqlite. 而Go語(yǔ)言的網(wǎng)站上也列出了更多的的cgo包.

最后, 如果你對(duì)于cgo的內(nèi)部是怎么運(yùn)作這個(gè)事情感到好奇的話, 去看看運(yùn)行時(shí)包的cgocall.c文件的注釋吧.

Golang項(xiàng)目部署3,容器部署

容器部署即使用 docker 化部署 golang 應(yīng)用程序,這是在云服務(wù)時(shí)代最流行的部署方式,也是最推薦的部署方式。

跨平臺(tái)交叉編譯是 golang 的特點(diǎn)之一,可以非常方便地編譯出我們需要的目標(biāo)服務(wù)器平臺(tái)的版本,而且是靜態(tài)編譯,非常容易地解決了運(yùn)行依賴問題。

使用以下指令可以靜態(tài)編譯 Linux 平臺(tái) amd64 架構(gòu)的可執(zhí)行文件:

生成的 main 便是我們靜態(tài)編譯的,可部署于 Linux amd64 上的可執(zhí)行文件。

我們需要將該可執(zhí)行文件 main 編譯生成 docker 鏡像,以便于分發(fā)及部署。 Golang 的運(yùn)行環(huán)境推薦使用 alpine 基礎(chǔ)系統(tǒng)鏡像,編譯出的容器鏡像約為 20MB 左右。

一個(gè)參考的 Dockerfile 文件如下:

其中,我們的基礎(chǔ)鏡像使用了 loads/alpine:3.8 ,中國(guó)國(guó)內(nèi)的用戶推薦使用該基礎(chǔ)鏡像,基礎(chǔ)鏡像的 Dockerfile 地址: ,倉(cāng)庫(kù)地址:

隨后使用 " docker build -t main . " 指令編譯生成名為 main 的 docker 鏡像。

需要注意的是,在某些項(xiàng)目的架構(gòu)設(shè)計(jì)中, 靜態(tài)文件 和 配置文件 可能不會(huì)隨著鏡像進(jìn)行編譯發(fā)布,而是分開進(jìn)行管理和發(fā)布。

例如,使用 MVVM 模式的項(xiàng)目中(例如使用 vue 框架),往往是前后端非常獨(dú)立的,因此在鏡像中往往并不會(huì)包含 public 目錄。而使用了 配置管理中心 (例如使用 consul / etcd / zookeeper )的項(xiàng)目中,也往往并不需要 config 目錄。

因此對(duì)于以上示例的 Dockerfile 的使用,僅作參考,根據(jù)實(shí)際情況請(qǐng)進(jìn)行必要的調(diào)整。

使用以下指令可直接運(yùn)行剛才編譯成的鏡像:

容器的分發(fā)可以使用 docker 官方的平臺(tái): ,國(guó)內(nèi)也可以考慮使用阿里云: 。

在企業(yè)級(jí)生產(chǎn)環(huán)境中, docker 容器往往需要結(jié)合 kubernetes 或者 docker swarm 容器編排工具一起使用。

容器編排涉及到的內(nèi)容比較多,感興趣的同學(xué)可以參考以下資料:

go語(yǔ)言到底有什么好處

1. 部署簡(jiǎn)單

Go

編譯生成的是一個(gè)靜態(tài)可執(zhí)行文件,除了glibc外沒有其他外部依賴。這讓部署變得異常方便:目標(biāo)機(jī)器上只需要一個(gè)基礎(chǔ)的系統(tǒng)和必要的管理、監(jiān)控工具,完全不需要操心應(yīng)用所需的各種包、庫(kù)的依賴關(guān)系,大大減輕了維護(hù)的負(fù)擔(dān)。

2. 并發(fā)性好

Goroutine和channel使得編寫高并發(fā)的服務(wù)端軟件變得相當(dāng)容易,很多情況下完全不需要考慮鎖機(jī)制以及由此帶來的各種問題。單個(gè)Go應(yīng)用也能有效的利用多個(gè)CPU核,并行執(zhí)行的性能好。

3. 良好的語(yǔ)言設(shè)計(jì)

從學(xué)術(shù)的角度講Go語(yǔ)言其實(shí)非常平庸,不支持許多高級(jí)的語(yǔ)言特性;但從工程的角度講,Go的設(shè)計(jì)是非常優(yōu)秀的:規(guī)范足夠簡(jiǎn)單靈活,有其他語(yǔ)言基礎(chǔ)的程序員都能迅速上手。更重要的是

Go 自帶完善的工具鏈,大大提高了團(tuán)隊(duì)協(xié)作的一致性。

4. 執(zhí)行性能好

雖然不如 C 和 Java,但相比于其他編程語(yǔ)言,其執(zhí)行性能還是很好的,適合編寫一些瓶頸業(yè)務(wù),內(nèi)存占用也非常省。

golang map源碼淺析

golang 中 map的實(shí)現(xiàn)結(jié)構(gòu)為: 哈希表 + 鏈表。 其中鏈表,作用是當(dāng)發(fā)生hash沖突時(shí),拉鏈法生成的結(jié)點(diǎn)。

可以看到, []bmap 是一個(gè)hash table, 每一個(gè) bmap是我們常說的“桶”。 經(jīng)過hash 函數(shù)計(jì)算出來相同的hash值, 放到相同的桶中。 一個(gè) bmap中可以存放 8個(gè) 元素, 如果多出8個(gè),則生成新的結(jié)點(diǎn),尾接到隊(duì)尾。

以上是只是靜態(tài)文件 src/runtime/map.go 中的定義。 實(shí)際上編譯期間會(huì)給它加料 ,動(dòng)態(tài)地創(chuàng)建一個(gè)新的結(jié)構(gòu):

上圖就是 bmap的內(nèi)存模型, HOB Hash 指的就是 top hash。 注意到 key 和 value 是各自放在一起的,并不是 key/value/key/value/... 這樣的形式。源碼里說明這樣的好處是在某些情況下可以省略掉 padding 字段,節(jié)省內(nèi)存空間。

每個(gè) bmap設(shè)計(jì)成 最多只能放 8 個(gè) key-value 對(duì) ,如果有第 9 個(gè) key-value 落入當(dāng)前的 bmap,那就需要再構(gòu)建一個(gè) bmap,通過 overflow 指針連接起來。

map創(chuàng)建方法:

我們實(shí)際上是通過調(diào)用的 makemap ,來創(chuàng)建map的。實(shí)際工作只是初始化了hmap中的各種字段,如:設(shè)置B的大小, 設(shè)置hash 種子 hash 0.

注意 :

makemap 返回是*hmap 指針, 即 map 是引用對(duì)象, 對(duì)map的操作會(huì)影響到結(jié)構(gòu)體內(nèi)部 。

使用方式

對(duì)應(yīng)的是下面兩種方法

map的key的類型,實(shí)現(xiàn)了自己的hash 方式。每種類型實(shí)現(xiàn)hash函數(shù)方式不一樣。

key 經(jīng)過哈希計(jì)算后得到hash值,共 64 個(gè) bit 位。 其中后B 個(gè)bit位置, 用來定位當(dāng)前元素落在哪一個(gè)桶里, 高8個(gè)bit 為當(dāng)前 hash 值的top hash。 實(shí)際上定位key的過程是一個(gè)雙重循環(huán)的過程, 外層循環(huán)遍歷 所有的overflow, 內(nèi)層循環(huán)遍歷 當(dāng)前bmap 中的 8個(gè)元素 。

舉例說明: 如果當(dāng)前 B 的值為 5, 那么buckets 的長(zhǎng)度 為 2^5 = 32。假設(shè)有個(gè)key 經(jīng)過hash函數(shù)計(jì)算后,得到的hash結(jié)果為:

外層遍歷bucket 中的鏈表

內(nèi)層循環(huán)遍歷 bmap中的8個(gè) cell

建議先不看此部分內(nèi)容,看完后續(xù) 修改 map中元素 - 擴(kuò)容 操作后 再回頭看此部分內(nèi)容。

擴(kuò)容前的數(shù)據(jù):

等量擴(kuò)容后的數(shù)據(jù):

等量擴(kuò)容后,查找方式和原本相同, 不多做贅述。

兩倍擴(kuò)容后的數(shù)據(jù)

兩倍擴(kuò)容后,oldbuckets 的元素,可能被分配成了兩部分。查找順序如下:

此處只分析 mapaccess1 ,。 mapaccess2 相比 mapaccess1 多添加了是否找到的bool值, 有興趣可自行看一下。

使用方式:

步驟如下:

擴(kuò)容條件 :

擴(kuò)容的標(biāo)識(shí) : h.oldbuckets != nil

假設(shè)當(dāng)前定位到了新的buckets的3號(hào)桶中,首先會(huì)判斷oldbuckets中的對(duì)應(yīng)的桶有沒有被搬遷過。 如果搬遷過了,不需要看原來的桶了,直接遍歷新的buckets的3號(hào)桶。

擴(kuò)容前:

等量擴(kuò)容結(jié)果

雙倍擴(kuò)容會(huì)將old buckets上的元素分配到x, y兩個(gè)部key 1 B == 0 分配到x部分,key 1 B == 1 分配到y(tǒng)部分

注意: 當(dāng)前只對(duì)雙倍擴(kuò)容描述, 等量擴(kuò)容只是重新填充了一下元素, 相對(duì)位置沒有改變。

假設(shè)當(dāng)前map 的B == 5,原本元素經(jīng)過hash函數(shù)計(jì)算的 hash 值為:

因?yàn)殡p倍擴(kuò)容之后 B = B + 1,此時(shí)B == 6。key 1 B == 1, 即 當(dāng)前元素rehash到高位,新buckets中 y 部分. 否則 key 1 B == 0 則rehash到低位,即x 部分。

使用方式:

可以看到,每一遍歷生成迭代器的時(shí)候,會(huì)隨機(jī)選取一個(gè)bucket 以及 一個(gè)cell開始。 從前往后遍歷,再次遍歷到起始位置時(shí),遍歷完成。

Golang 的靜態(tài)編譯

Go 語(yǔ)言和 C 語(yǔ)言的一個(gè)很大的區(qū)別是, Go 語(yǔ)言只靜態(tài)編譯,做個(gè)測(cè)試:

一方面是 Go 語(yǔ)言編譯后的可執(zhí)行文件大小比 C 語(yǔ)言的大很多,

另一方面是 C 語(yǔ)言的可執(zhí)行文件需要依賴 glibc 動(dòng)態(tài)庫(kù),

用 ldd 命令可以看出來:

或者直接刪除 glibc 動(dòng)態(tài)庫(kù), C 可執(zhí)行程序報(bào)錯(cuò),而 Go 的還能運(yùn)行:

這時(shí)候只有內(nèi)部命令可以運(yùn)行,外部命令,包括 ln 甚至最常用的 ls 命令也不能運(yùn)行了:

設(shè)置好 LD_PRELOAD 環(huán)境變量之后, ln 命令可以運(yùn)行,但是 sudo 仍然不能運(yùn)行

只能靠 root 用戶來重新創(chuàng)建軟連接了:

所以用 sudo 來 rm 文件要小心,還是用 root 比較好。如果沒有預(yù)先留一個(gè)打開的 root 終端,登錄都登不進(jìn)去。


文章名稱:go語(yǔ)言靜態(tài)文件 go靜態(tài)編譯
本文地址:http://weahome.cn/article/hjeepc.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部