亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

golang中goexit的使用方法介紹

發布時間:2021-06-12 18:11:22 來源:億速云 閱讀:1094 作者:栢白 欄目:編程語言

這篇文章主要介紹了golang中goexit的使用方法介紹,具有一定借鑒價值,需要的朋友可以參考下。下面就和我一起來看看吧。

在學員群里,有同學在用 dlv 調試時看到了令人不解的 goexit:goexit 函數是啥,為啥 go fun(){}()  的上層是它?看著像是一個“退出”函數,為什么會出現在最上層?

其實如果看過 pprof 的火焰圖,也會經常看到 goexit 這個函數。

我們來個例子重現一下:

package main  import "time"  func main() {  go func ()  {   println("hello world")  }()    time.Sleep(10*time.Minute) }

啟動 dlv 調試,并分別在不同的地方打上斷點:

(dlv) b a.go:5  Breakpoint 1 (enabled) set at 0x106d12f for main.main() ./a.go:5 (dlv) b a.go:6 Breakpoint 2 (enabled) set at 0x106d13d for main.main() ./a.go:6 (dlv) b a.go:7 Breakpoint 3 (enabled) set at 0x106d1a0 for main.main.func1() ./a.go:7

執行命令 c 運行到斷點處,再執行 bt 命令得到 main 函數的調用棧:

(dlv) bt 0  0x000000000106d12f in main.main    at ./a.go:5 1  0x0000000001035c0f in runtime.main    at /usr/local/go/src/runtime/proc.go:204 2  0x0000000001064961 in runtime.goexit    at /usr/local/go/src/runtime/asm_amd64.s:1374

它的上一層是 runtime.main,找到原代碼位置,位于 src/runtime/proc.go 里的 main 函數,它是 Go 進程的 main  goroutine,這里會執行一些 init 操作、開啟 GC、執行用戶 main 函數……

fn := main_main // proc.go:203 fn() // proc.go:204

其中 fn 是 main_main 函數,表示用戶的 main 函數,執行到了這里,才真正將權力交給用戶。

繼續執行 c 命令和 bt 命令,得到 go 這一行的調用棧:

0  0x000000000106d13d in main.main    at ./a.go:6 1  0x0000000001035c0f in runtime.main    at /usr/local/go/src/runtime/proc.go:204 2  0x0000000001064961 in runtime.goexit    at /usr/local/go/src/runtime/asm_amd64.s:1374

以及 println 這一句的調用棧:

0  0x000000000106d1a0 in main.main.func1    at ./a.go:7 1  0x0000000001064961 in runtime.goexit    at /usr/local/go/src/runtime/asm_amd64.s:1374

可以看到,調用棧的最上層都是 runtime.goexit,我們跟著注明了的代碼行數,順藤摸瓜,找到 goexit 代碼:

// The top-most function running on a goroutine // returns to goexit+PCQuantum. TEXT runtime·goexit(SB),NOSPLIT,$0-0     BYTE    $0x90   // NOP     CALL    runtime·goexit1(SB) // does not return     // traceback from goexit1 must hit code range of goexit     BYTE    $0x90   // NOP

這還是個匯編函數,它接著調用 goexit1 函數、goexit0 函數,主要的功能就是將 goroutine 的各個字段清零,放入 gFree  隊列里,等待將來進行復用。

另一方面,goexit 函數的地址是在創建 goroutine 的過程中,塞到棧上的。讓 CPU “誤以為”:func() 是由 goexit  函數調用的。這樣一來,當 func() 執行完畢時,會返回到 goexit 函數做一些清理工作。

下面這張圖能看出在 newg 的棧底塞了一個 goexit 函數的地址:

golang中goexit的使用方法介紹

goexit 返回地址

對應的路徑是:

newporc -> newporc1 -> gostartcallfn -> gostartcall

來看 newproc1 中的關鍵幾行代碼:

newg.sched.pc = funcPC(goexit) + sys.PCQuantum newg.sched.g = guintptr(unsafe.Pointer(newg)) gostartcallfn(&newg.sched, fn)

這里的 newg 就是創建的 goroutine,每個新建的 goroutine 都會執行這些代碼。而 sched 結構體其實保存的是 goroutine  的執行現場,每當 goroutine 被調離 CPU,它的執行進度就是保存到這里。進度主要就是 SP、BP、PC,分別表示棧頂地址、棧底地址、指令位置,等  goroutine 再次得到 CPU 的執行權時,會把 SP、BP、PC 加載到寄存器中,從而從斷點處恢復運行。

回到上面的幾行代碼,pc 被賦值成了 funcPC(goexit),最后在 gostartcall 里:

// adjust Gobuf as if it executed a call to fn with context ctxt // and then did an immediate gosave. func gostartcall(buf *gobuf, fn, ctxt unsafe.Pointer) {  sp := buf.sp  ...  sp -= sys.PtrSize  *(*uintptr)(unsafe.Pointer(sp)) = buf.pc  buf.sp = sp  buf.pc = uintptr(fn)  buf.ctxt = ctxt }

sp 其實就是棧頂,第 7 行代碼把 buf.pc,也就是 goexit 的地址,放在了棧頂的地方,熟悉 Go 函數調用規約的朋友知道,這個位置其實就是  return addr,將來等 func() 執行完,就會回到父函數繼續執行,這里的父函數其實就是 goexit。

一切早已注定。

不過注意一點,main goroutine 和普通的 goroutine 不同的是,前者執行完用戶 main 函數后,會直接執行 exit  調用,整個進程退出:

golang中goexit的使用方法介紹

exit

也就不會進入 goexit 函數。而普通 goroutine 執行完畢后,則直接進入 goexit 函數,做一些清理工作。

這也就是為什么只要 main goroutine 執行完了,就不會等其他 goroutine,直接退出。一切都是因為 exit 這個調用。

今天我們主要講了 goexit 是怎么被安插到 goroutine 的棧上,從而實現 goroutine 執行完畢后再回到 goexit 函數。

以上就是golang中goexit的使用方法介紹的詳細內容了,看完之后是否有所收獲呢?如果想了解更多相關內容,歡迎來億速云行業資訊!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

博白县| 正定县| 科技| 黄平县| 彰化市| 枣庄市| 丘北县| 兴仁县| 科技| 微山县| 汉川市| 永新县| 南通市| 彭阳县| 南澳县| 嵊泗县| 宁南县| 扎赉特旗| 内江市| 花莲县| 瑞安市| 安康市| 瑞昌市| 仙桃市| 通城县| 沙洋县| 确山县| 汕尾市| 九江县| 东乌| 黄陵县| 咸阳市| 祁连县| 凤冈县| 禹城市| 奉化市| 和田县| 平果县| 托克逊县| 罗甸县| 福清市|