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)}
到这里,完成信号的注册,需要注意,当fn
为sighandler
时,会被替换成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
下图简单画出了信号的发送以及处理