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)}0asyncPreempt2有两个分支,需要注意下 分支1:
funcmstartm0(){...initsig(false)}1分支2:
funcmstartm0(){...initsig(false)}2funcmstartm0(){...initsig(false)}3发送信号
栈扫描流程
funcmstartm0(){...initsig(false)}4funcmstartm0(){...initsig(false)}5funcmstartm0(){...initsig(false)}6funcmstartm0(){...initsig(false)}7①: suspendG刚进来的时候,g为_Grunning,执行preemptM发送抢占信号_SIGURG ②: 下一次循环会走到这里,将stop设置为true,执行fallthrough继续执行到下个case代码块 ③: 将preemptStop设置为false,并且返回 到此,suspendG函数执行结束,执行scanstack开始栈扫描,再之后会执行resumeG恢复g
funcmstartm0(){...initsig(false)}8funcmstartm0(){...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))}}0funcinitsig(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))}}1preemptall -> 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下图简单画出了信号的发送以及处理