首页>>后端>>Golang->源码剖析go基于信号的抢占式调度

源码剖析go基于信号的抢占式调度

时间:2023-11-30 本站 点击:1

go version: 1.17 system: macos 信号的在程序中无非就是注册,发送信号以及信号的处理这三个流程,下面按照这个流程一个一个的进行分析

信号的注册

funcmstart1(){...if_g_.m==&m0{mstartm0()}...}
funcmstartm0(){...initsig(false)}
funcinitsig(preinitbool){if!preinit{//It'snowOKforsignalhandlerstorun.signalsOK=true}//Forc-archive/c-sharedthisiscalledbylibpreinitwith//preinit==true.if(isarchive||islibrary)&&!preinit{return}fori:=uint32(0);i<_NSIG;i++{t:=&sigtable[i]ift.flags==0||t.flags&_SigDefault!=0{continue}//Wedon'tneedtouseatomicoperationsherebecause//thereshouldn'tbeanyothergoroutinesrunningyet.fwdSig[i]=getsig(i)//检这个信号是否需要设置signalhandlerif!sigInstallGoHandler(i){//Evenifwearenotinstallingasignalhandler,//setSA_ONSTACKifnecessary.iffwdSig[i]!=_SIG_DFL&&fwdSig[i]!=_SIG_IGN{setsigstack(i)}elseiffwdSig[i]==_SIG_IGN{sigInitIgnored(i)}continue}handlingSig[i]=1setsig(i,funcPC(sighandler))}}

通过setsig来进行对某一个信号设置sighandler

funcsetsig(iuint32,fnuintptr){varsausigactiontsa.sa_flags=_SA_SIGINFO|_SA_ONSTACK|_SA_RESTARTsa.sa_mask=^uint32(0)iffn==funcPC(sighandler){//funcPC(sighandler)matchesthecallersinsignal_unix.goifiscgo{fn=abi.FuncPCABI0(cgoSigtramp)}else{//sighandler被替换为sigtrampfn=abi.FuncPCABI0(sigtramp)}}*(*uintptr)(unsafe.Pointer(&sa.__sigaction_u))=fn//在这里进行信号的注册,当收到信号时候,会调用sigtrampsigaction(i,&sa,nil)}

到这里,完成信号的注册,需要注意,当fnsighandler时,会被替换成sigtramp

收到信号后的处理

//Thisisthefunctionregisteredduringsigactionandisinvokedwhen//asignalisreceived.ItjustredirectstotheGofunctionsigtrampgo.//CalledusingCABI.TEXTruntime·sigtramp(SB),NOSPLIT,$0//TransitionfromCABItoGoABI.PUSH_REGS_HOST_TO_ABI0()//CallintotheGosignalhandlerNOPSP//disablevetstackcheckingADJSP$24MOVLDI,0(SP)//sigMOVQSI,8(SP)//infoMOVQDX,16(SP)//ctxCALL·sigtrampgo(SB)ADJSP$-24POP_REGS_HOST_TO_ABI0()RET

收到信号后,会调用之前注册的函数sigtramp,并继续调用sigtrampgo

funcsigtrampgo(siguint32,info*siginfo,ctxunsafe.Pointer){ifsigfwdgo(sig,info,ctx){return}c:=&sigctxt{info,ctx}g:=sigFetchG(c)setg(g)...sighandler(sig,info,ctx,g)setg(g)...
funcsighandler(siguint32,info*siginfo,ctxtunsafe.Pointer,gp*g){_g_:=getg()c:=&sigctxt{info,ctxt}ifsig==_SIGPROF{sigprof(c.sigpc(),c.sigsp(),c.siglr(),gp,_g_.m)return}ifsig==_SIGTRAP&&testSigtrap!=nil&&testSigtrap(info,(*sigctxt)(noescape(unsafe.Pointer(c))),gp){return}ifsig==_SIGUSR1&&testSigusr1!=nil&&testSigusr1(gp){return}//代表是一个抢占信号ifsig==sigPreempt&&debug.asyncpreemptoff==0{//Mightbeapreemptionsignal.doSigPreempt(gp,c)//Evenifthiswasdefinitelyapreemptionsignal,it//mayhavebeencoalescedwithanothersignal,sowe//stillletitthroughtotheapplication.}}

收到的信号是抢占信号,则调用doSigPreempt

//doSigPreempthandlesapreemptionsignalongp.funcdoSigPreempt(gp*g,ctxt*sigctxt){//CheckifthisGwantstobepreemptedandissafeto//preempt.//检查g是否要被抢占,并且能被安全的抢占ifwantAsyncPreempt(gp){ifok,newpc:=isAsyncSafePoint(gp,ctxt.sigpc(),ctxt.sigsp(),ctxt.siglr());ok{//AdjustthePCandinjectacalltoasyncPreempt.ctxt.pushCall(funcPC(asyncPreempt),newpc)}}//Acknowledgethepreemption.atomic.Xadd(&gp.m.preemptGen,1)atomic.Store(&gp.m.signalPending,0)ifGOOS=="darwin"||GOOS=="ios"{atomic.Xadd(&pendingPreemptSignals,-1)}}
func(c*sigctxt)pushCall(targetPC,resumePCuintptr){//MakeitlooklikewecalledtargetatresumePC.sp:=uintptr(c.rsp())sp-=sys.PtrSize*(*uintptr)(unsafe.Pointer(sp))=resumePCc.set_rsp(uint64(sp))c.set_rip(uint64(targetPC))}

pushCall这个函数很关键,他通过改变PC寄存器,从而执行抢占调用

TEXT·asyncPreempt<ABIInternal>(SB),NOSPLIT|NOFRAME,$0-0PUSHQBPMOVQSP,BP//SaveflagsbeforeclobberingthemPUSHFQ//objdoesn'tunderstandADD/SUBonSP,butdoesunderstandADJSPADJSP$368//Butvetdoesn'tknowADJSP,sosuppressvetstackcheckingNOPSPMOVQAX,0(SP)MOVQCX,8(SP)...保存现场,其实就是把所有寄存器的值存起来MOVQR14,96(SP)MOVQR15,104(SP)#ifdefGOOS_darwinCMPBinternal∕cpu·X86+const_offsetX86HasAVX(SB),$0JE2(PC)VZEROUPPER#endifMOVUPSX0,112(SP)MOVUPSX1,128(SP)MOVUPSX2,144(SP)MOVUPSX3,160(SP)MOVUPSX4,176(SP)MOVUPSX5,192(SP)MOVUPSX6,208(SP)MOVUPSX7,224(SP)MOVUPSX8,240(SP)MOVUPSX9,256(SP)MOVUPSX10,272(SP)MOVUPSX11,288(SP)MOVUPSX12,304(SP)MOVUPSX13,320(SP)MOVUPSX14,336(SP)MOVUPSX15,352(SP)CALL·asyncPreempt2(SB)MOVUPS352(SP),X15MOVUPS336(SP),X14...恢复之前保存的所有寄存器的值MOVQ8(SP),CXMOVQ0(SP),AXADJSP$-368POPFQPOPQBPRET

asyncPreempt做的事就是保存现场,调用asyncPreempt2,再恢复现场

funcmstartm0(){...initsig(false)}0

asyncPreempt2有两个分支,需要注意下 分支1:

funcmstartm0(){...initsig(false)}1

分支2:

funcmstartm0(){...initsig(false)}2
funcmstartm0(){...initsig(false)}3

发送信号

栈扫描流程

funcmstartm0(){...initsig(false)}4
funcmstartm0(){...initsig(false)}5
funcmstartm0(){...initsig(false)}6
funcmstartm0(){...initsig(false)}7

: suspendG刚进来的时候,g为_Grunning,执行preemptM发送抢占信号_SIGURG : 下一次循环会走到这里,将stop设置为true,执行fallthrough继续执行到下个case代码块 : 将preemptStop设置为false,并且返回 到此,suspendG函数执行结束,执行scanstack开始栈扫描,再之后会执行resumeG恢复g

funcmstartm0(){...initsig(false)}8
funcmstartm0(){...initsig(false)}9

其他流程

sysmon -> retake -> preemptone

funcinitsig(preinitbool){if!preinit{//It'snowOKforsignalhandlerstorun.signalsOK=true}//Forc-archive/c-sharedthisiscalledbylibpreinitwith//preinit==true.if(isarchive||islibrary)&&!preinit{return}fori:=uint32(0);i<_NSIG;i++{t:=&sigtable[i]ift.flags==0||t.flags&_SigDefault!=0{continue}//Wedon'tneedtouseatomicoperationsherebecause//thereshouldn'tbeanyothergoroutinesrunningyet.fwdSig[i]=getsig(i)//检这个信号是否需要设置signalhandlerif!sigInstallGoHandler(i){//Evenifwearenotinstallingasignalhandler,//setSA_ONSTACKifnecessary.iffwdSig[i]!=_SIG_DFL&&fwdSig[i]!=_SIG_IGN{setsigstack(i)}elseiffwdSig[i]==_SIG_IGN{sigInitIgnored(i)}continue}handlingSig[i]=1setsig(i,funcPC(sighandler))}}0
funcinitsig(preinitbool){if!preinit{//It'snowOKforsignalhandlerstorun.signalsOK=true}//Forc-archive/c-sharedthisiscalledbylibpreinitwith//preinit==true.if(isarchive||islibrary)&&!preinit{return}fori:=uint32(0);i<_NSIG;i++{t:=&sigtable[i]ift.flags==0||t.flags&_SigDefault!=0{continue}//Wedon'tneedtouseatomicoperationsherebecause//thereshouldn'tbeanyothergoroutinesrunningyet.fwdSig[i]=getsig(i)//检这个信号是否需要设置signalhandlerif!sigInstallGoHandler(i){//Evenifwearenotinstallingasignalhandler,//setSA_ONSTACKifnecessary.iffwdSig[i]!=_SIG_DFL&&fwdSig[i]!=_SIG_IGN{setsigstack(i)}elseiffwdSig[i]==_SIG_IGN{sigInitIgnored(i)}continue}handlingSig[i]=1setsig(i,funcPC(sighandler))}}1

preemptall -> preemptone

funcinitsig(preinitbool){if!preinit{//It'snowOKforsignalhandlerstorun.signalsOK=true}//Forc-archive/c-sharedthisiscalledbylibpreinitwith//preinit==true.if(isarchive||islibrary)&&!preinit{return}fori:=uint32(0);i<_NSIG;i++{t:=&sigtable[i]ift.flags==0||t.flags&_SigDefault!=0{continue}//Wedon'tneedtouseatomicoperationsherebecause//thereshouldn'tbeanyothergoroutinesrunningyet.fwdSig[i]=getsig(i)//检这个信号是否需要设置signalhandlerif!sigInstallGoHandler(i){//Evenifwearenotinstallingasignalhandler,//setSA_ONSTACKifnecessary.iffwdSig[i]!=_SIG_DFL&&fwdSig[i]!=_SIG_IGN{setsigstack(i)}elseiffwdSig[i]==_SIG_IGN{sigInitIgnored(i)}continue}handlingSig[i]=1setsig(i,funcPC(sighandler))}}2

下图简单画出了信号的发送以及处理


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:/Golang/4593.html