G
1 .goroutine的新建,休眠,恢復(fù),停止都會(huì)受到go運(yùn)行時(shí)的管理
2 .goroutine執(zhí)行異步操作時(shí),等操作完成之后在恢復(fù),不會(huì)占用系統(tǒng)線程
3 .goroutine新建或者恢復(fù)會(huì)添加到運(yùn)行隊(duì)列,等待M取出并運(yùn)行
4 .G的狀態(tài)
1 .空閑中:idle:G剛創(chuàng)建,沒有初始化
2 .待運(yùn)行:runnable:g正在運(yùn)行隊(duì)列中,等待M取出并運(yùn)行
3 .運(yùn)行中:running:M正在運(yùn)行這個(gè)G,這時(shí)M會(huì)擁有一個(gè)P
4 .系統(tǒng)調(diào)用:syscall:表示這個(gè)M這個(gè)在運(yùn)行這個(gè)g發(fā)起的系統(tǒng)調(diào)用,這時(shí)M并不擁有p
5 .等待中waiting:表示g在等待某些條件完成。此時(shí)g不在運(yùn)行也不再運(yùn)行隊(duì)列中,可能在channel等待隊(duì)列中
6 .已終止:dead,表示G未被使用,可能已經(jīng)執(zhí)行完畢
7 .棧復(fù)制中:copystack,表示g正在獲取一個(gè)新的??臻g并把原來的內(nèi)容復(fù)制過去,用于防止GC掃描
5.G里面重要的成員
1.stack:當(dāng)前g使用的??臻g
2.stackguard0:檢查當(dāng)前??臻g是否有足夠的值,低于這個(gè)值就擴(kuò)張棧,0是go代碼使用的
3.stackguard1:檢查??臻g是否有足夠的值,如果低于這個(gè)值就會(huì)擴(kuò)張棧,1是原生代碼使用的
4.m:對應(yīng)g當(dāng)前的m
5.sched:g的調(diào)度數(shù)據(jù),當(dāng)g中斷時(shí)會(huì)保存當(dāng)前的pc,rsp等值到這里,恢復(fù)運(yùn)行時(shí)使用這里的值
6.atomicstatus:g當(dāng)前的狀態(tài)
7.schedlink:下一個(gè)g,當(dāng)g在鏈表結(jié)構(gòu)中會(huì)使用
8.preempt:g是否被搶占中
9.lockedm:g是否要求要回到這個(gè)M執(zhí)行,有時(shí)g中斷之后會(huì)要求使用原來的m來執(zhí)行
M
1 .在當(dāng)前golang中等于系統(tǒng)線程
2 .m運(yùn)行以下代碼:go代碼,即goroutine,M運(yùn)行g(shù)o代碼需要一個(gè)p提供環(huán)境。原生代碼,例如阻塞的syscall.m運(yùn)行原生代碼不需要p
3 .m會(huì)從運(yùn)行隊(duì)列中取出G,然后運(yùn)行G,如果G運(yùn)行完畢或者進(jìn)入休眠狀態(tài),則從隊(duì)列中取出下一個(gè)g運(yùn)行,周而復(fù)始。
4 .有時(shí)g需要調(diào)用一些無法避免阻塞的原生代碼,這個(gè)時(shí)候m會(huì)釋放持有的p并進(jìn)入阻塞狀態(tài),其他m會(huì)取得這個(gè)p并繼續(xù)運(yùn)行隊(duì)列中的G
5 .go需要保證有足夠多的m來運(yùn)行G,不會(huì)讓cpu閑著,也會(huì)保證m的數(shù)量不能太多
6 .m的狀態(tài)
1 .自旋中:m正在從運(yùn)行隊(duì)列獲取g,這個(gè)時(shí)候m會(huì)擁有一個(gè)p,是否喚醒或者創(chuàng)建新的m取決于當(dāng)前自旋中的m的數(shù)量
2.執(zhí)行g(shù)o代碼:m正在執(zhí)行g(shù)o代碼,這時(shí)m會(huì)有一個(gè)p
3.執(zhí)行原生代碼:m正在執(zhí)行原生代碼或者阻塞的syscall,這時(shí)m沒有p
4.休眠中:m發(fā)現(xiàn)無待運(yùn)行的g會(huì)進(jìn)入休眠,并添加到空閑m鏈表中,這時(shí)m沒有p
5.
7.空閑m鏈表
1.進(jìn)入休眠的M會(huì)等待一個(gè)信號量m.park,喚醒休眠的m會(huì)使用這個(gè)信號量
2.go需要保證有足夠的m可以運(yùn)行g(shù)
3.入隊(duì)待運(yùn)行的G之后,如果當(dāng)前無自旋的M但是有空閑的P,就喚醒或者新建一個(gè)M
4.M離開自旋狀態(tài)并運(yùn)行出隊(duì)的G時(shí)(出隊(duì)是指從本地或者全局隊(duì)列中出隊(duì)在m上運(yùn)行),如果當(dāng)前無自選的但是有空閑的p,就喚醒或者新建一個(gè)m
5.當(dāng)m離開自旋狀態(tài)準(zhǔn)備休眠時(shí),會(huì)在離開后再次檢查所有運(yùn)行隊(duì)列,如果有待運(yùn)行的g重新進(jìn)入自旋狀態(tài)
6.
8.M里面重要的成員
1 .g0用于調(diào)度特殊的g,調(diào)度和執(zhí)行系統(tǒng)時(shí)會(huì)切換到這個(gè)g
2.curg:當(dāng)前運(yùn)行的g
3.p:當(dāng)前擁有的p
4.nextp:喚醒m時(shí),m會(huì)有這個(gè)p
5.park:M休眠時(shí)使用的信號量,喚醒M時(shí)會(huì)通過他喚醒
6.schedllink:下一個(gè)m,當(dāng)m在鏈表結(jié)構(gòu)中會(huì)使用
7.mache:分配內(nèi)存時(shí)使用的本地分配器,和p.mache一樣
8.lockedg:lockedm的對應(yīng)值
p
1.代表M運(yùn)行G需要的資源
2.p的數(shù)量默認(rèn)為是cpu核數(shù),但是可以動(dòng)態(tài)設(shè)置
3.p就是控制go代碼并行度的機(jī)制。
4.如果p的數(shù)量等于1,那就證明當(dāng)前最多有一個(gè)線程來執(zhí)行g(shù)o代碼
5.執(zhí)行原生代碼的線程數(shù)量是不受p的控制
6.因?yàn)橥粋€(gè)時(shí)間只有一個(gè)m可以有p,此時(shí)p的數(shù)據(jù)都是鎖自由的,所以讀寫這些數(shù)據(jù)非常效率高
7.p的狀態(tài)
1.空閑中idle:當(dāng)m發(fā)現(xiàn)無待運(yùn)行的G時(shí)會(huì)進(jìn)入休眠,這時(shí)M擁有的p會(huì)變?yōu)榭臻e并加入到空閑p鏈表中
2.運(yùn)行中running:當(dāng)擁有一個(gè)p之后,這個(gè)p的狀態(tài)就會(huì)變?yōu)檫\(yùn)行中,M執(zhí)行G會(huì)使用這個(gè)p中的資源
3.系統(tǒng)調(diào)用中:syscall當(dāng)go調(diào)用原生代碼的時(shí)候,原生代碼反過來又調(diào)用go代碼時(shí),使用的p會(huì)變成此狀態(tài)
4.GC停止中:當(dāng)gc停止了整個(gè)世界時(shí),p會(huì)變?yōu)檫@個(gè)狀態(tài)
5.dead已停止:當(dāng)p的數(shù)量在運(yùn)行時(shí)改變,且數(shù)量減少時(shí)多余的p會(huì)變?yōu)檫@個(gè)狀態(tài)
8.空閑p鏈表
1.當(dāng)本地的運(yùn)行隊(duì)列中的所有G都運(yùn)行完畢,又不能從其他地方拿到G時(shí)
2.擁有p的M會(huì)釋放p并進(jìn)入休眠狀態(tài),釋放的p會(huì)變?yōu)榭臻e狀態(tài)并加入空閑p鏈表中,空閑p鏈表保存在全局變量sched中
3.下次待運(yùn)行的G入隊(duì)時(shí)如果發(fā)現(xiàn)有空閑的p,但是有沒有自旋中的M會(huì)喚醒或者新建一個(gè)m,m會(huì)擁有這個(gè)p.p會(huì)重新變成運(yùn)行中
9.p里面重要的值
1.status:p當(dāng)前的狀態(tài)
2.link:下一個(gè)p,當(dāng)p在鏈表結(jié)構(gòu)中會(huì)使用
3.m:擁有這個(gè)p的m
4.mache:分配內(nèi)存時(shí)使用的本地分配器
5.runqhead:本地運(yùn)行隊(duì)列的出隊(duì)序號
6.runqtail:本地運(yùn)行隊(duì)列的入隊(duì)序號
7.gfree:G的自由列表,保存為_Gdead后可以復(fù)用g的實(shí)例
8.gcBgMarkWorker:后臺CG的worker函數(shù),如果它存在M會(huì)優(yōu)先執(zhí)行他
9.gcw:GC的本地功作隊(duì)列
本地運(yùn)行隊(duì)列
1.在go中有多個(gè)運(yùn)行隊(duì)列可以保存運(yùn)行的G,分別是p的本地隊(duì)列和全局隊(duì)列
2.入隊(duì)待運(yùn)行的G,會(huì)優(yōu)先添加到當(dāng)前p的本地隊(duì)列,m獲取待運(yùn)行的G時(shí)也會(huì)優(yōu)先從擁有的p的本地隊(duì)列獲取
3.本地運(yùn)行隊(duì)列有數(shù)量上限,超過256個(gè)會(huì)入隊(duì)到全局隊(duì)列
4.本地隊(duì)列入隊(duì)和出隊(duì)不需要使用線程鎖
5.本地隊(duì)列是一個(gè)環(huán)形隊(duì)列,長256的數(shù)組和兩個(gè)序號組成
6.當(dāng)m從p的本地運(yùn)行隊(duì)列獲取G時(shí),如果發(fā)現(xiàn)本地隊(duì)列為空,會(huì)從其他的p盜取一半的G過來
全局隊(duì)列
1.全局隊(duì)列保存在全局變量sched中,全局運(yùn)行隊(duì)列入隊(duì)和出隊(duì)需要使用線程鎖
M0和G0
1.go中有特殊的M和G,分別是M0和G0
2.m0是騎寵程序后的主線程,這個(gè)m對應(yīng)的實(shí)例會(huì)在全局變量m0中,不需要再heap上分配
3.m0負(fù)責(zé)執(zhí)行初始化操作和啟動(dòng)第一個(gè)g,之后m0就和其他的m一樣了
4.g0僅是負(fù)責(zé)調(diào)度的g,g0不指向任何可執(zhí)行的函數(shù),每個(gè)m都會(huì)有一個(gè)自己的g0
5.在調(diào)度或者系統(tǒng)調(diào)用的時(shí)候會(huì)使用g0的??臻g,全局變量的g0是m0的g0