本文目錄一覽:
- 1、golang 進程創建,fork,以及熱重啟(無縫升級)
- 2、golang服務器比測視機慢
- 3、Golang入門到項目實戰 | golang簡介及安裝
- 4、Golang 的靜態編譯
- 5、Golang實驗性功能SetMaxHeap 固定值GC
- 6、如何配置go語言開發環境
golang 進程創建,fork,以及熱重啟(無縫升級)
一般來說,進程的操作使用的是一些系統的命令,所以go內部使用os包,進行一些運行系統命令的操作
os 包及其子包 os/exec 提供了創建進程的方法。
一般的,應該優先使用 os/exec 包。因為 os/exec 包依賴 os 包中關鍵創建進程的 API,為了便於理解,我們先探討 os 包中和進程相關的部分。
Unix :fork創建一個進程,(及其一些變種,如 vfork、clone)。
Go:Linux 下創建進程使用的系統調用是 clone。
允許一進程(父進程)創建一新進程(子進程)。具體做法是,新的子進程幾近於對父進程的翻版:子進程獲得父進程的棧、數據段、堆和執行文本段的拷貝。可將此視為把父進程一分為二。
終止一進程,將進程佔用的所有資源(內存、文件描述符等)歸還內核,交其進行再次分配。參數 status 為一整型變量,表示進程的退出狀態。父進程可使用系統調用 wait() 來獲取該狀態。
目的有二:其一,如果子進程尚未調用 exit() 終止,那麼 wait 會掛起父進程直至子進程終止;其二,子進程的終止狀態通過 wait 的 status 參數返回。
加載一個新程序(路徑名為 pathname,參數列表為 argv,環境變量列表為 envp)到當前進程的內存。這將丟棄現存的程序文本段,並為新程序重新創建棧、數據段以及堆。通常將這一動作稱為執行一個新程序。
沒有直接提供 fork 系統調用的封裝,而是將 fork 和 execve 合二為一,提供了 syscall.ForkExec。如果想只調用 fork,得自己通過 syscall.Syscall(syscall.SYS_FORK, 0, 0, 0) 實現。
os.Process 存儲了通過 StartProcess 創建的進程的相關信息。
一般通過 StartProcess 創建 Process 的實例,函數聲明如下:
它使用提供的程序名、命令行參數、屬性開始一個新進程。StartProcess 是一個低級別的接口。os/exec 包提供了高級別的接口,一般應該盡量使用 os/exec 包。如果出錯,錯誤的類型會是 *PathError。
屬性定義如下:
FindProcess 可以通過 pid 查找一個運行中的進程。該函數返回的 Process 對象可以用於獲取關於底層操作系統進程的信息。在 Unix 系統中,此函數總是成功,即使 pid 對應的進程不存在。
Process 提供了四個方法:Kill、Signal、Wait 和 Release。其中 Kill 和 Signal 跟信號相關,而 Kill 實際上就是調用 Signal,發送了 SIGKILL 信號,強制進程退出,關於信號,後續章節會專門講解。
Release 方法用於釋放 Process 對象相關的資源,以便將來可以被再使用。該方法只有在確定沒有調用 Wait 時才需要調用。Unix 中,該方法的內部實現只是將 Process 的 pid 置為 -1。
通過 os 包可以做到運行外部命令,如前面的例子。不過,Go 標準庫為我們封裝了更好用的包: os/exec,運行外部命令,應該優先使用它,它包裝了 os.StartProcess 函數以便更容易的重定向標準輸入和輸出,使用管道連接 I/O,以及作其它的一些調整。
exec.LookPath 函數在 PATH 指定目錄中搜索可執行程序,如 file 中有 /,則只在當前目錄搜索。該函數返回完整路徑或相對於當前路徑的一個相對路徑。
func LookPath(file string) (string, error)
如果在 PATH 中沒有找到可執行文件,則返回 exec.ErrNotFound。
Cmd 結構代表一個正在準備或者在執行中的外部命令,調用了 Run、Output 或 CombinedOutput 後,Cmd 實例不能被重用。
一般的,應該通過 exec.Command 函數產生 Cmd 實例:
用法
得到 * Cmd 實例後,接下來一般有兩種寫法:
前面講到,通過 Cmd 實例後,有兩種方式運行命令。有時候,我們不只是簡單的運行命令,還希望能控制命令的輸入和輸出。通過上面的 API 介紹,控制輸入輸出有幾種方法:
參考資料:
golang服務器比測視機慢
解決辦法:①關閉殺毒軟件 (首先嘗試)。②設置 gopoxy代理。
具體的設置如下:設置環境變量: 變量名GOPROXY,變量值:關閉 vscode 之後,重新打開程序,然後運行。如果還是很慢,連結果都不出的話,重新檢查程序,看看程序的循環是不是有問題,或者是程序本身就沒有輸出。
Golang入門到項目實戰 | golang簡介及安裝
Go(又稱 Golang)是 Google 的 Robert Griesemer,Rob Pike 及 Ken Thompson 開發的一種靜態強類型、編譯型語言。Go 語言語法與 C 相近,但功能上有:內存安全,GC(垃圾回收),結構形態及 CSP-style 並發計算。
go語言特點
go語言的應用領域
哪些公司(項目)在使用go語言
下載開發包
windows下安裝
1.打開下載的msi可執行文件,根據提示進行安裝。默認會安裝在c:/Program Files/go目錄下面。會自動添加go可執行文件環境變量。
2.驗證安裝情況
a.打開命令行
b. 輸入$ go version
linux下安裝
1.在/usr/local/下面創建一個目錄go
2.下載壓縮文件到該目錄(/usr/local/go),並解壓縮
3.添加/usr/local/go/bin到PATH環境變量,打開$HOME/.profile 或者/etc/profile輸入如下內容:
4.執行如下命令使得配置文件及時生效
驗證
1.如果你的mac有Homebrew包管理工具,可以使用它來安裝
2.如果沒有下載mac安裝包,根據提示安裝
3.默認安裝在/usr/local/go下面
4.設置環境變量,同Linux
Golang 的靜態編譯
Go 語言和 C 語言的一個很大的區別是, Go 語言只靜態編譯,做個測試:
一方面是 Go 語言編譯後的可執行文件大小比 C 語言的大很多,
另一方面是 C 語言的可執行文件需要依賴 glibc 動態庫,
用 ldd 命令可以看出來:
或者直接刪除 glibc 動態庫, C 可執行程序報錯,而 Go 的還能運行:
這時候只有內部命令可以運行,外部命令,包括 ln 甚至最常用的 ls 命令也不能運行了:
設置好 LD_PRELOAD 環境變量之後, ln 命令可以運行,但是 sudo 仍然不能運行
只能靠 root 用戶來重新創建軟連接了:
所以用 sudo 來 rm 文件要小心,還是用 root 比較好。如果沒有預先留一個打開的 root 終端,登錄都登不進去。
Golang實驗性功能SetMaxHeap 固定值GC
簡單來說, SetMaxHeap 提供了一種可以設置固定觸發閾值的 GC (Garbage Collection垃圾回收)方式
官方源碼鏈接
大量臨時對象分配導致的 GC 觸發頻率過高, GC 後實際存活的對象較少,
或者機器內存較充足,希望使用剩餘內存,降低 GC 頻率的場景
GC 會 STW ( Stop The World ),對於時延敏感場景,在一個周期內連續觸發兩輪 GC ,那麼 STW 和 GC 佔用的 CPU 資源都會造成很大的影響, SetMaxHeap 並不一定是完美的,在某些場景下做了些權衡,官方也在進行相關的實驗,當前方案仍沒有合入主版本。
先看下如果沒有 SetMaxHeap ,對於如上所述的場景的解決方案
這裡簡單說下 GC 的幾個值的含義,可通過 GODEBUG=gctrace=1 獲得如下數據
這裡只關注 128-132-67 MB 135 MB goal ,
分別為 GC開始時內存使用量 – GC標記完成時內存使用量 – GC標記完成時的存活內存量 本輪GC標記完成時的 預期 內存使用量(上一輪 GC 完成時確定)
引用 GC peace設計文檔 中的一張圖來說明
對應關係如下:
簡單說下 GC pacing (信用機制)
GC pacing 有兩個目標,
那麼當一輪 GC 完成時,如何只根據本輪 GC 存活量去實現這兩個小目標呢?
這裡實際是根據當前的一些數據或狀態去 預估 「未來」,所有會存在些誤差
首先確定 gc Goal goal = memstats.heap_marked + memstats.heap_marked*uint64(gcpercent)/100
heap_marked 為本輪 GC 存活量, gcpercent 默認為 100 ,可以通過環境變量 GOGC=100 或者 debug.SetGCPercent(100) 來設置
那麼默認情況下 goal = 2 * heap_marked
gc_trigger 是與 goal 相關的一個值( gc_trigger 大約為 goal 的 90% 左右),每輪 GC 標記完成時,會根據 |Ha-Hg| 和實際使用的 cpu 資源 動態調整 gc_trigger 與 goal 的差值
goal 與 gc_trigger 的差值即為,為 GC 期間分配的對象所預留的空間
GC pacing 還會預估下一輪 GC 發生時,需要掃描對象對象的總量,進而換算為下一輪 GC 所需的工作量,進而計算出 mark assist 的值
本輪 GC 觸發( gc_trigger ),到本輪的 goal 期間,需要儘力完成 GC mark 標記操作,所以當 GC 期間,某個 goroutine 分配大量內存時,就會被拉去做 mark assist 工作,先進行 GC mark 標記賺取足夠的信用值後,才能分配對應大小的對象
根據本輪 GC 存活的內存量( heap_marked )和下一輪 GC 觸發的閾值( gc_trigger )計算 sweep assist 的值,本輪 GC 完成,到下一輪 GC 觸發( gc_trigger )時,需要儘力完成 sweep 清掃操作
預估下一輪 GC 所需的工作量的方式如下:
繼續分析文章開頭的問題,如何充分利用剩餘內存,降低 GC 頻率和 GC 對 CPU 的資源消耗
如上圖可以看出, GC 後,存活的對象為 2GB 左右,如果將 gcpercent 設置為 400 ,那麼就可以將下一輪 GC 觸發閾值提升到 10GB 左右
前面一輪看起來很好,提升了 GC 觸發的閾值到 10GB ,但是如果某一輪 GC 後的存活對象到達 2.5GB 的時候,那麼下一輪 GC 觸發的閾值,將會超過內存閾值,造成 OOM ( Out of Memory ),進而導致程序崩潰。
可以通過 GOGC=off 或者 debug.SetGCPercent(-1) 來關閉 GC
可以通過進程外監控內存使用狀態,使用信號觸發的方式通知程序,或 ReadMemStats 、或 linkname runtime.heapRetained 等方式進行堆內存使用的監測
可以通過調用 runtime.GC() 或者 debug.FreeOSMemory() 來手動進行 GC 。
這裡還需要說幾個事情來解釋這個方案所存在的問題
通過 GOGC=off 或者 debug.SetGCPercent(-1) 是如何關閉 GC 的?
gc 4 @1.006s 0%: 0.033+5.6+0.024 ms clock, 0.27+4.4/11/25+0.19 ms cpu, 428-428-16 MB, 17592186044415 MB goal, 8 P (forced)
通過 GC trace 可以看出,上面所說的 goal 變成了一個很詭異的值 17592186044415
實際上關閉 GC 後, Go 會將 goal 設置為一個極大值 ^uint64(0) ,那麼對應的 GC 觸發閾值也被調成了一個極大值,這種處理方式看起來也沒什麼問題,將閾值調大,預期永遠不會再觸發 GC
那麼如果在關閉 GC 的情況下,手動調用 runtime.GC() 會導致什麼呢?
由於 goal 和 gc_trigger 被設置成了極大值, mark assist 和 sweep assist 也會按照這個錯誤的值去計算,導致工作量預估錯誤,這一點可以從 trace 中進行證明
可以看到很詭異的 trace 圖,這裡不做深究,該方案與 GC pacing 信用機制不兼容
記住,不要在關閉 GC 的情況下手動觸發 GC ,至少在當前 Go1.14 版本中仍存在這個問題
SetMaxHeap 的實現原理,簡單來說是強行控制了 goal 的值
註: SetMaxHeap ,本質上是一個軟限制,並不能解決 極端場景 下的 OOM ,可以配合內存監控和 debug.FreeOSMemory() 使用
SetMaxHeap 控制的是堆內存大小, Go 中除了堆內存還分配了如下內存,所以實際使用過程中,與實際硬件內存閾值之間需要留有一部分餘量。
對於文章開始所述問題,使用 SetMaxHeap 後,預期的 GC 過程大概是這個樣子
簡單用法1
該方法簡單粗暴,直接將 goal 設置為了固定值
註:通過上文所講,觸發 GC 實際上是 gc_trigger ,所以當閾值設置為 12GB 時,會提前一點觸發 GC ,這裡為了描述方便,近似認為 gc_trigger=goal
簡單用法2
當不關閉 GC 時, SetMaxHeap 的邏輯是, goal 仍按照 gcpercent 進行計算,當 goal 小於 SetMaxHeap 閾值時不進行處理;當 goal 大於 SetMaxHeap 閾值時,將 goal 限制為 SetMaxHeap 閾值
註:通過上文所講,觸發 GC 實際上是 gc_trigger ,所以當閾值設置為 12GB 時,會提前一點觸發 GC ,這裡為了描述方便,近似認為 gc_trigger=goal
切換到 go1.14 分支,作者選擇了 git checkout go1.14.5
選擇官方提供的 cherry-pick 方式(可能需要梯子,文件改動不多,我後面會列出具體改動)
git fetch “” refs/changes/67/227767/3 git cherry-pick FETCH_HEAD
需要重新編譯Go源碼
注意點:
下面源碼中的官方注釋說的比較清楚,在一些關鍵位置加入了中文注釋
入參bytes為要設置的閾值
notify 簡單理解為 GC 的策略 發生變化時會向 channel 發送通知,後續源碼可以看出「策略」具體指哪些內容
返回值為本次設置之前的 MaxHeap 值
$GOROOT/src/runtime/debug/garbage.go
$GOROOT/src/runtime/mgc.go
註:作者盡量用通俗易懂的語言去解釋 Go 的一些機制和 SetMaxHeap 功能,可能有些描述與實現細節不完全一致,如有錯誤還請指出
如何配置go語言開發環境
1、下載go的zip文件。並且一定要把文件解壓到c:\go目錄下。
2、配置windows的高級環境變量。包括:GOROOT、GOOS、GOBIN、GOARCH。並且在path變量裏面把c:\go\bin加入。以便可以在命令行直接運行go命令。
舉例:我的機器:
GOPATH= c:\go;c:\go\src;F:\workspace\goSample01;
GOBIN=c:\go\bin;F:\workspace\goSample01\bin;
其中,c:\go是go的安裝路徑;
F:\workspace\goSample01是我寫的go語言項目的工程目錄;
F:\workspace\goSample01\bin是go語言項目的工程目錄下的可執行文件路徑;
3、在完成環境變量配置後,打開一個命令行窗口,直接輸入go,然後回車,看看是否出現go的幫助信息。如果出現,那麼go的基本環境就OK了。
注意:這個基本環境不包含開發工具,也不能直接編譯帶C代碼的go程序。
4、
(可選)為了支持Import遠程包,最好裝個gomingw。下載地址:
/downloads/list。如果下的是壓縮包,請把它解壓到C盤。例如,C:\gowin-env。裏面有個Console.bat是以後使用go
get的環境。舉例:有個文件a.go,裏面import(
“fmt”
“github.com/astaxie/beedb”
_ “github.com/ziutek/mymysql/godrv”
為了編譯該a.go文件,需要啟動Console.bat,然後在該命令行窗口,進入c:\go\src目錄下,執行go getgithub.com/astaxie/beedb
Go get github.com/ziutek/mymysql/godrv .
Go會自動下載該遠程包並編譯和安裝這些包。
配置goclipse(可選)
(如果不喜歡eclipse開發工具,請跳過這個配置。)
1、下載並安裝goclipse插件。Goclipse是go語言for eclipse的插件,下載地址:
2、啟動eclipse並創建go項目。然後寫個最簡單的helloworld.go文件,並運行。代碼如下:
packagemainimport”fmt”func main(){ fmt.Printf(“hello, world”)}
配置gocode(可選)
如果不需要go語法輔助和eclipse裏面的(按ALT+/)彈出go語言自動輔助功能,請跳過這個配置。
1、下載gocode的zip文件,解壓後放在go的bin目錄下。
2、下載並安裝Git軟件。並且在path裏面配置git的執行路徑。例如c:\git\bin
3、在命令行執行:go build .\gocode。如果一切正常,那麼將會編譯生成一個gocode.exe文件在go的bin目錄下。如果編譯失敗,那麼就轉第4步。
4、如果第3步直接編譯gocode源文件成功,那就直接到第5步。否則,就需要通過git下載gocode源文件,然後再編譯。在命令行執行:go get -u github.com/nsf/gocode 。就會生成gocode.exe文件。
5、在goclipse插件裏面指定gocode的路徑。就可以在elcipse裏面調用gocode來幫助寫編碼了。
從開發工具這塊看,go語言還不夠成熟,開發工具都還不完善,有待改進。
下載go-tour教程源代碼(可選)
Google有個在線運行go語言的教程(),很不錯。支持在web上直接運行大部分的go程序,想了解這個教程的源代碼的朋友可以通過以下方式獲取。如果沒興趣,可以跳過這個步驟。
1、下載安裝Mercurial軟件。
2、在命令行下輸入:
hg clone
作為測試用的。如果把http改成https協議,下載就會失敗。搞不懂。
編譯帶調用C代碼的go文件(可選)
1、為了在windows下編譯帶C代碼的go程序,你首先需要下載並安裝MinGW或者Cygwin。
2、首選安裝MinGW。在安裝MinGW之後,記得要把MinGW安裝目錄\bin路徑設置在path環境變量裏面,以便能在dos窗口下直接調用gcc。
3、下載一個gowin-env。下載地址:gowin-env。下載後解壓到某個目錄下,例如:C:\gowin-env. 然後,編輯go-env.bat。配置相關的go參數。例如,我的配置是:
set GOARCH=386
set GOOS=windows
set GOROOT=c:\go
set GOBIN=%GOROOT%\bin
set GOPATH=%GOROOT%;F:\workspace\goSample01;
設置好go-env.bat後,就可以點擊Console.bat來啟動編譯和運行窗口。
4、編寫一個帶C代碼的go程序。例如,testc.go
5、編譯
例如:
go build -compiler gccgo test_c.go
運行調用C代碼的go文件(可選)
1、testc.go.
創建rand目錄,然後在rand裏面創建testc.go. 代碼如下:
package rand
/*
//
#include stdio.h
*/
import “C”
func PrintHello() {
C.puts(C.CString(“Hello, world\n”))
}
2、a.go
在rand下創建a.go.代碼如下:
package rand
import “fmt”
func SayHello(name string){
fmt.Println(name)
}
3、test_import.go
在rand的上一級創建test_import.go。代碼如下:
package main
import “./rand”
func main(){
rand.SayHello(“tom”)
rand.PrintHello()
}
4、運行test_import.go
go run test_import.go
在測試其它幾個C代碼的時候,發現windows版本的cgo還有些編譯問題,同樣的代碼轉移到蘋果的XCODE下就沒有問題。後來終於發現原因了,原來有些例子是unix平台下的,而在windows平台下,方法名和參數需要做調整。
例如:下面代碼在windows下編譯報一堆錯誤。
package rand
/*
#include stdlib.h
*/
import “C”
func Random() int {
return int(C.random())
}
func Seed(i int) {
C.srandom(C.uint(i))
}
這裡需要把return int(C.random()) 修改為「return int(C.rand())」
C.srandom(C.uint(i))修改為「C.srand(C.uint(i))」編譯就OK了。
原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-hk/n/279873.html