初始化
image.png
g生命周期
image.png
newproc函數(shù)
這段代碼的邏輯如下:
- 函數(shù)newproc接收一個指向funcval類型的指針fn作為參數(shù)娱俺。
- 獲取當(dāng)前的goroutine(gp)和調(diào)用者的程序計數(shù)器(pc)。
- 在系統(tǒng)棧上運行一個函數(shù)废麻,該函數(shù)會執(zhí)行以下操作:
○ 調(diào)用newproc1函數(shù)荠卷,以fn、gp和pc作為參數(shù)烛愧,創(chuàng)建一個新的goroutine(newg)油宜。
○ 獲取當(dāng)前的P(處理器)的指針(pp)。
○ 將新創(chuàng)建的goroutine(newg)放入P的運行隊列(runq)中怜姿。
○ 如果main函數(shù)已經(jīng)開始運行(mainStarted為真)慎冤,則喚醒P。
總結(jié):這段代碼的目的是創(chuàng)建一個新的goroutine沧卢,并將其放入P的運行隊列中蚁堤,然后根據(jù)需要喚醒P。
_StackMin = 2048
newproc1函數(shù)
這段代碼是Go語言中用于創(chuàng)建新的goroutine的代碼但狭。下面是代碼的邏輯解釋:
- 首先披诗,檢查傳入的函數(shù)指針是否為nil,如果是nil立磁,則調(diào)用fatal函數(shù)報錯呈队。
- 調(diào)用acquirem函數(shù)獲取當(dāng)前goroutine所在的m對象,并禁用搶占唱歧,因為之后會使用m和p局部變量宪摧。
- 獲取當(dāng)前p對象。
- 調(diào)用gfget函數(shù)從p中獲取一個空閑的g對象,如果沒有空閑的g對象绍刮,則調(diào)用malg函數(shù)創(chuàng)建一個新的g對象(_StackMin)温圆。
- 檢查新的g對象是否已經(jīng)分配了棧空間孩革,如果沒有岁歉,則拋出錯誤。
- 檢查新的g對象的狀態(tài)是否為_Gdead膝蜈,如果不是锅移,則拋出錯誤。
- 計算需要分配給新的g對象的棻ゲ空間的大小非剃,并對齊到系統(tǒng)的棧對齊大小。
- 根據(jù)是否使用LR寄存器推沸,設(shè)置調(diào)用者的LR寄存器的值备绽,并準備調(diào)用goexit函數(shù)的棧幀。
- 使用memclrNoHeapPointers函數(shù)清零新的g對象的sched字段鬓催,并設(shè)置sched.sp和newg.stktopsp的值為棧頂?shù)刂贰?/li>
- 設(shè)置sched.pc為goexit函數(shù)的地址(加上PCQuantum以確保前一條指令在同一個函數(shù)中)肺素。
- 調(diào)用gostartcallfn函數(shù)啟動新的goroutine,并傳入sched和函數(shù)指針宇驾。
- 設(shè)置新的g對象的gopc字段為調(diào)用者的PC值倍靡。
- 保存調(diào)用者的callergp對象,并設(shè)置為新的g對象的ancestors字段课舍。
- 設(shè)置新的g對象的startpc字段為函數(shù)的地址塌西。
- 如果新的g對象是系統(tǒng)goroutine,則增加系統(tǒng)goroutine計數(shù)筝尾。
- 如果當(dāng)前m對象的curg字段不為nil捡需,則將其標簽賦值給新的g對象的labels字段。
- 如果goroutine profile處于活動狀態(tài)忿等,將新的g對象的goroutineProfiled字段設(shè)置為goroutineProfileSatisfied栖忠。
- 生成一個隨機數(shù),并檢查是否滿足跟蹤條件(每隔一定周期進行跟蹤)贸街,如果滿足庵寞,則將新的g對象的tracking字段設(shè)置為true。
- 將新的g對象的狀態(tài)由_Gdead變?yōu)開Grunnable薛匪。
- 將新的g對象的棧添加到GC控制器的可掃描堆棧列表中捐川。
- 檢查p對象的goidcache是否已滿,如果滿了逸尖,則重新分配一批goid古沥。
- 為新的g對象分配一個唯一的goid瘸右,并將p的goidcache遞增。
- 如果啟用了race檢測岩齿,調(diào)用racegostart函數(shù)為新的g對象啟動race檢測太颤,并初始化raceignore字段。
- 如果新的g對象的labels字段不為nil盹沈,則調(diào)用racereleasemergeg函數(shù)與信號處理程序中的讀取同步龄章。
- 如果啟用了trace,調(diào)用traceGoCreate函數(shù)記錄goroutine的創(chuàng)建乞封。
- 調(diào)用releasem函數(shù)釋放m對象做裙。
- 返回新的g對象。
m生命周期
image.png
schedule函數(shù)
這段代碼是Go語言運行時調(diào)度器的核心邏輯肃晚。它用于在多個M(線程)之間分配可運行的G(協(xié)程)锚贱。
代碼的主要邏輯如下:
- 獲取當(dāng)前G所屬的M(線程)。
- 如果M持有鎖定关串,則拋出異常拧廊。
- 如果M鎖定了G,則停止當(dāng)前M悍缠,并執(zhí)行被鎖定的G卦绣,這個過程不會返回耐量。
- 如果M正在執(zhí)行cgo調(diào)用飞蚓,則拋出異常。
- 進入循環(huán)廊蜒,直到找到可運行的G趴拧。
- 如果M處于旋轉(zhuǎn)狀態(tài)(spinning)但本地運行隊列不為空,則拋出異常山叮。
- 調(diào)用findRunnable()函數(shù)查找可運行的G著榴,該函數(shù)會阻塞直到有可運行的G。
- 如果M處于旋轉(zhuǎn)狀態(tài)屁倔,則重置該狀態(tài)并可能啟動新的旋轉(zhuǎn)M脑又。
- 如果調(diào)度器被禁用且當(dāng)前G不允許被調(diào)度,則將其放入待運行列表锐借,并繼續(xù)查找下一個可運行的G问麸。
- 如果需要喚醒一個P(處理器),則喚醒它钞翔。
- 如果當(dāng)前G鎖定了M严卖,則將當(dāng)前的P交給鎖定的M,并阻塞等待一個新的P布轿。
- 執(zhí)行可運行的G哮笆,繼續(xù)下一輪調(diào)度来颤。
總體上,這段代碼的邏輯是通過循環(huán)不斷地在多個M之間分配可運行的G稠肘,并根據(jù)不同的情況進行相應(yīng)的處理福铅。
sysmon
image.png
sysmon函數(shù)
這段代碼是Go調(diào)度器的sysmon函數(shù),它是一個系統(tǒng)監(jiān)控的循環(huán)项阴。以下是代碼邏輯的解釋:
- 初始化變量和計數(shù)器:初始化lasttrace本讥、idle和delay等變量。
- 進入循環(huán):進入一個無限循環(huán)鲁冯,直到程序退出拷沸。
- 計算延遲時間:根據(jù)idle的值計算延遲時間delay。如果idle為0薯演,則延遲時間為20微秒撞芍。如果idle大于50,則延遲時間加倍跨扮。如果延遲時間超過10毫秒序无,則將其設(shè)置為10毫秒。
- 睡眠:根據(jù)延遲時間衡创,調(diào)用usleep函數(shù)進入睡眠狀態(tài)帝嗡。
- 檢查是否需要喚醒:根據(jù)一些條件判斷是否需要喚醒sysmon。如果滿足條件璃氢,使用notetsleep函數(shù)進行睡眠哟玷,并在喚醒時重置idle和delay的值。
- 鎖定sysmonlock:獲取sysmonlock的互斥鎖一也。
- 觸發(fā)libc攔截器:如果需要巢寡,觸發(fā)libc的攔截器。
- 網(wǎng)絡(luò)輪詢:如果距離上次輪詢網(wǎng)絡(luò)超過10毫秒椰苟,調(diào)用netpoll函數(shù)進行網(wǎng)絡(luò)輪詢抑月。如果返回的列表不為空,調(diào)用injectglist函數(shù)將這些goroutine添加到可運行隊列中舆蝴。
- 處理netbsd的定時器:如果是netbsd操作系統(tǒng)谦絮,并且存在定時器處理的bug,調(diào)用startm函數(shù)啟動一個新的M來處理定時器洁仗。
- 喚醒scavenger:如果有人請求喚醒scavenger层皱,調(diào)用scavenger的wake函數(shù)。
- 重新獲取P和搶占:調(diào)用retake函數(shù)重新獲取被阻塞在系統(tǒng)調(diào)用中的P京痢,并搶占運行時間較長的G奶甘。如果成功獲取到P,則將idle重置為0祭椰,否則idle遞增臭家。
- 強制GC:如果滿足觸發(fā)GC的條件疲陕,并且沒有正在進行的GC,調(diào)用forcegc函數(shù)進行強制GC钉赁。
- 調(diào)試輸出:如果調(diào)試級別大于0蹄殃,并且距離上次輸出的時間大于等于指定的間隔時間,調(diào)用schedtrace函數(shù)進行調(diào)度跟蹤你踩。
- 解鎖sysmonlock:釋放sysmonlock的互斥鎖诅岩。
以上是sysmon函數(shù)的邏輯解釋,它主要負責(zé)監(jiān)控系統(tǒng)狀態(tài)带膜,并根據(jù)條件進行相應(yīng)的處理吩谦,例如喚醒、輪詢網(wǎng)絡(luò)膝藕、重新獲取P等式廷。
retake函數(shù)
該段代碼是一個用于調(diào)度處理器(P)的函數(shù)。它首先獲取一個鎖(allpLock)來確保在處理過程中不會發(fā)生allp片段的更改芭挽。然后滑废,它遍歷所有的P(處理器),對于每個P袜爪,它執(zhí)行以下操作:
- 檢查P是否為nil蠕趁,如果是,則跳過該P辛馆。
- 檢查P的狀態(tài)(status)俺陋。如果狀態(tài)為_Prunning或_Psyscall,表示P正在運行或正在執(zhí)行系統(tǒng)調(diào)用怀各。
- 如果P的調(diào)度計數(shù)(schedtick)發(fā)生變化倔韭,說明P在運行時間過長,需要搶占它瓢对。并記錄當(dāng)前時間和搶占時間。
- 如果P的搶占時間加上forcePreemptNS(超過一定時間)小于當(dāng)前時間胰苏,表示需要搶占G(goroutine)硕蛹。調(diào)用preemptone函數(shù)搶占G。如果狀態(tài)為_Psyscall硕并,則還需要設(shè)置sysretake為true法焰。
- 如果狀態(tài)為_Psyscall,表示P正在執(zhí)行系統(tǒng)調(diào)用倔毙。檢查系統(tǒng)調(diào)用計數(shù)(syscalltick)埃仪,如果發(fā)生變化,記錄當(dāng)前時間和系統(tǒng)調(diào)用時間陕赃,并跳過該P卵蛉。
- 檢查是否存在其他任務(wù)需要處理颁股,以及當(dāng)前是否有空閑的M(線程),并且系統(tǒng)調(diào)用時間加上一定時間(至少20微秒)大于當(dāng)前時間傻丝。如果是甘有,則跳過該P。
- 釋放allpLock鎖葡缰,以便獲取sched.lock鎖亏掀。
- 在執(zhí)行CAS(Compare-and-Swap)操作之前,需要減少空閑的M的數(shù)量(假裝有一個額外的M正在運行)泛释,以防止從我們重新獲取的M退出系統(tǒng)調(diào)用滤愕,增加nmidle并報告死鎖。
- 如果CAS操作成功怜校,將P的狀態(tài)更改為_Pidle该互,增加n的計數(shù),并將syscalltick遞增韭畸,并將P移交給其他M處理宇智。
- 增加空閑的M的數(shù)量。
- 重新獲取allpLock鎖胰丁。
最后随橘,該函數(shù)返回n的值,表示成功搶占的P的數(shù)量锦庸。