這篇文章主要介紹了golang中g(shù)oexit的使用方法介紹,具有一定借鑒價(jià)值,需要的朋友可以參考下。下面就和我一起來(lái)看看吧。
創(chuàng)新互聯(lián)建站主要為客戶提供服務(wù)項(xiàng)目涵蓋了網(wǎng)頁(yè)視覺(jué)設(shè)計(jì)、VI標(biāo)志設(shè)計(jì)、成都全網(wǎng)營(yíng)銷推廣、網(wǎng)站程序開(kāi)發(fā)、HTML5響應(yīng)式網(wǎng)站建設(shè)公司、手機(jī)網(wǎng)站制作設(shè)計(jì)、微商城、網(wǎng)站托管及成都網(wǎng)站維護(hù)、WEB系統(tǒng)開(kāi)發(fā)、域名注冊(cè)、國(guó)內(nèi)外服務(wù)器租用、視頻、平面設(shè)計(jì)、SEO優(yōu)化排名。設(shè)計(jì)、前端、后端三個(gè)建站步驟的完善服務(wù)體系。一人跟蹤測(cè)試的建站服務(wù)標(biāo)準(zhǔn)。已經(jīng)為混凝土攪拌罐車(chē)行業(yè)客戶提供了網(wǎng)站推廣服務(wù)。
在學(xué)員群里,有同學(xué)在用 dlv 調(diào)試時(shí)看到了令人不解的 goexit:goexit 函數(shù)是啥,為啥 go fun(){}() 的上層是它?看著像是一個(gè)“退出”函數(shù),為什么會(huì)出現(xiàn)在最上層?
其實(shí)如果看過(guò) pprof 的火焰圖,也會(huì)經(jīng)??吹?goexit 這個(gè)函數(shù)。
我們來(lái)個(gè)例子重現(xiàn)一下:
package main import "time" func main() { go func () { println("hello world") }() time.Sleep(10*time.Minute) }
啟動(dòng) dlv 調(diào)試,并分別在不同的地方打上斷點(diǎn):
(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
執(zhí)行命令 c 運(yùn)行到斷點(diǎn)處,再執(zhí)行 bt 命令得到 main 函數(shù)的調(diào)用棧:
(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 函數(shù),它是 Go 進(jìn)程的 main goroutine,這里會(huì)執(zhí)行一些 init 操作、開(kāi)啟 GC、執(zhí)行用戶 main 函數(shù)……
fn := main_main // proc.go:203 fn() // proc.go:204
其中 fn 是 main_main 函數(shù),表示用戶的 main 函數(shù),執(zhí)行到了這里,才真正將權(quán)力交給用戶。
繼續(xù)執(zhí)行 c 命令和 bt 命令,得到 go 這一行的調(diào)用棧:
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 這一句的調(diào)用棧:
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
可以看到,調(diào)用棧的最上層都是 runtime.goexit,我們跟著注明了的代碼行數(shù),順藤摸瓜,找到 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
這還是個(gè)匯編函數(shù),它接著調(diào)用 goexit1 函數(shù)、goexit0 函數(shù),主要的功能就是將 goroutine 的各個(gè)字段清零,放入 gFree 隊(duì)列里,等待將來(lái)進(jìn)行復(fù)用。
另一方面,goexit 函數(shù)的地址是在創(chuàng)建 goroutine 的過(guò)程中,塞到棧上的。讓 CPU “誤以為”:func() 是由 goexit 函數(shù)調(diào)用的。這樣一來(lái),當(dāng) func() 執(zhí)行完畢時(shí),會(huì)返回到 goexit 函數(shù)做一些清理工作。
下面這張圖能看出在 newg 的棧底塞了一個(gè) goexit 函數(shù)的地址:
goexit 返回地址
對(duì)應(yīng)的路徑是:
newporc -> newporc1 -> gostartcallfn -> gostartcall
來(lái)看 newproc1 中的關(guān)鍵幾行代碼:
newg.sched.pc = funcPC(goexit) + sys.PCQuantum newg.sched.g = guintptr(unsafe.Pointer(newg)) gostartcallfn(&newg.sched, fn)
這里的 newg 就是創(chuàng)建的 goroutine,每個(gè)新建的 goroutine 都會(huì)執(zhí)行這些代碼。而 sched 結(jié)構(gòu)體其實(shí)保存的是 goroutine 的執(zhí)行現(xiàn)場(chǎng),每當(dāng) goroutine 被調(diào)離 CPU,它的執(zhí)行進(jìn)度就是保存到這里。進(jìn)度主要就是 SP、BP、PC,分別表示棧頂?shù)刂贰5椎刂?、指令位置,? goroutine 再次得到 CPU 的執(zhí)行權(quán)時(shí),會(huì)把 SP、BP、PC 加載到寄存器中,從而從斷點(diǎn)處恢復(fù)運(yùn)行。
回到上面的幾行代碼,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 其實(shí)就是棧頂,第 7 行代碼把 buf.pc,也就是 goexit 的地址,放在了棧頂?shù)牡胤?,熟?Go 函數(shù)調(diào)用規(guī)約的朋友知道,這個(gè)位置其實(shí)就是 return addr,將來(lái)等 func() 執(zhí)行完,就會(huì)回到父函數(shù)繼續(xù)執(zhí)行,這里的父函數(shù)其實(shí)就是 goexit。
一切早已注定。
不過(guò)注意一點(diǎn),main goroutine 和普通的 goroutine 不同的是,前者執(zhí)行完用戶 main 函數(shù)后,會(huì)直接執(zhí)行 exit 調(diào)用,整個(gè)進(jìn)程退出:
exit
也就不會(huì)進(jìn)入 goexit 函數(shù)。而普通 goroutine 執(zhí)行完畢后,則直接進(jìn)入 goexit 函數(shù),做一些清理工作。
這也就是為什么只要 main goroutine 執(zhí)行完了,就不會(huì)等其他 goroutine,直接退出。一切都是因?yàn)?exit 這個(gè)調(diào)用。
今天我們主要講了 goexit 是怎么被安插到 goroutine 的棧上,從而實(shí)現(xiàn) goroutine 執(zhí)行完畢后再回到 goexit 函數(shù)。
以上就是golang中g(shù)oexit的使用方法介紹的詳細(xì)內(nèi)容了,看完之后是否有所收獲呢?如果想了解更多相關(guān)內(nèi)容,歡迎來(lái)創(chuàng)新互聯(lián)行業(yè)資訊!
新聞名稱:golang中g(shù)oexit的使用方法介紹
文章分享:http://muchs.cn/article20/pieejo.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供品牌網(wǎng)站建設(shè)、商城網(wǎng)站、網(wǎng)站收錄、網(wǎng)站營(yíng)銷、微信小程序、網(wǎng)站制作
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)