// One round of scheduler: find a goroutine and run it. // The argument is the goroutine that was running before // schedule was called, or nil if this is the first call. // Never returns. static void schedule(G *gp) { int32 hz; uint32 v; schedlock(); if(gp != nil) { // Just finished running gp. gp->m = nil; runtime·sched.grunning--; // atomic { mcpu-- } v = runtime·xadd(&runtime·sched.atomic, -1<<mcpuShift); if(atomic_mcpu(v) > maxgomaxprocs) runtime·throw("negative mcpu in scheduler"); switch(gp->status){ case Grunnable: case Gdead: // Shouldn't have been running! runtime·throw("bad gp->status in sched"); case Grunning: gp->status = Grunnable; gput(gp); break; case Gmoribund: gp->status = Gdead; if(gp->lockedm) { gp->lockedm = nil; m->lockedg = nil; } gp->idlem = nil; unwindstack(gp, nil); gfput(gp); if(--runtime·sched.gcount == 0) runtime·exit(0); break; } if(gp->readyonstop){ gp->readyonstop = 0; readylocked(gp); } } else if(m->helpgc) { // Bootstrap m or new m started by starttheworld. // atomic { mcpu-- } v = runtime·xadd(&runtime·sched.atomic, -1<<mcpuShift); if(atomic_mcpu(v) > maxgomaxprocs) runtime·throw("negative mcpu in scheduler"); // Compensate for increment in starttheworld(). runtime·sched.grunning--; m->helpgc = 0; } else if(m->nextg != nil) { // New m started by matchmg. } else { runtime·throw("invalid m state in scheduler"); } // Find (or wait for) g to run. Unlocks runtime·sched. gp = nextgandunlock(); gp->readyonstop = 0; gp->status = Grunning; m->curg = gp; gp->m = m; // Check whether the profiler needs to be turned on or off. hz = runtime·sched.profilehz; if(m->profilehz != hz) runtime·resetcpuprofiler(hz); if(gp->sched.pc == (byte*)runtime·goexit) { // kickoff runtime·gogocall(&gp->sched, (void(*)(void))gp->entry); } runtime·gogo(&gp->sched, 0); }
// Called from reflect·call or from runtime·morestack when a new // stack segment is needed. Allocate a new stack big enough for // m->moreframesize bytes, copy m->moreargsize bytes to the new frame, // and then act as though runtime·lessstack called the function at // m->morepc. void runtime·newstack(void) { int32 framesize, argsize; Stktop *top; byte *stk, *sp; G *g1; Gobuf label; bool reflectcall; uintptr free; framesize = m->moreframesize; argsize = m->moreargsize; g1 = m->curg; if(m->morebuf.sp < g1->stackguard - StackGuard) { runtime·printf("runtime: split stack overflow: %p < %p\n", m->morebuf.sp, g1->stackguard - StackGuard); runtime·throw("runtime: split stack overflow"); } if(argsize % sizeof(uintptr) != 0) { runtime·printf("runtime: stack split with misaligned argsize %d\n", argsize); runtime·throw("runtime: stack split argsize"); } reflectcall = framesize==1; if(reflectcall) framesize = 0; if(reflectcall && m->morebuf.sp - sizeof(Stktop) - argsize - 32 > g1->stackguard) { // special case: called from reflect.call (framesize==1) // to call code with an arbitrary argument size, // and we have enough space on the current stack. // the new Stktop* is necessary to unwind, but // we don't need to create a new segment. top = (Stktop*)(m->morebuf.sp - sizeof(*top)); stk = g1->stackguard - StackGuard; free = 0; } else { // allocate new segment. framesize += argsize; framesize += StackExtra; // room for more functions, Stktop. if(framesize < StackMin) framesize = StackMin; framesize += StackSystem; stk = runtime·stackalloc(framesize); top = (Stktop*)(stk+framesize-sizeof(*top)); free = framesize; } //runtime·printf("newstack framesize=%d argsize=%d morepc=%p moreargp=%p gobuf=%p, %p top=%p old=%p\n", //framesize, argsize, m->morepc, m->moreargp, m->morebuf.pc, m->morebuf.sp, top, g1->stackbase); top->stackbase = g1->stackbase; top->stackguard = g1->stackguard; top->gobuf = m->morebuf; top->argp = m->moreargp; top->argsize = argsize; top->free = free; m->moreargp = nil; m->morebuf.pc = nil; m->morebuf.sp = nil; // copy flag from panic top->panic = g1->ispanic; g1->ispanic = false; g1->stackbase = (byte*)top; g1->stackguard = stk + StackGuard; sp = (byte*)top; if(argsize > 0) { sp -= argsize; runtime·memmove(sp, top->argp, argsize); } if(thechar == '5') { // caller would have saved its LR below args. sp -= sizeof(void*); *(void**)sp = nil; } // Continue as if lessstack had just called m->morepc // (the PC that decided to grow the stack). label.sp = sp; label.pc = (byte*)runtime·lessstack; label.g = m->curg; runtime·gogocall(&label, m->morepc); *(int32*)345 = 123; // never return }
// Called from reflect·call or from runtime·morestack when a new // stack segment is needed. Allocate a new stack big enough for // m->moreframesize bytes, copy m->moreargsize bytes to the new frame, // and then act as though runtime·lessstack called the function at // m->morepc. void runtime·newstack(void) { int32 framesize, minalloc, argsize; Stktop *top; byte *stk, *sp; uintptr *src, *dst, *dstend; G *gp; Gobuf label; bool reflectcall; uintptr free; framesize = m->moreframesize; argsize = m->moreargsize; gp = m->curg; if(m->morebuf.sp < gp->stackguard - StackGuard) { runtime·printf("runtime: split stack overflow: %p < %p\n", m->morebuf.sp, gp->stackguard - StackGuard); runtime·throw("runtime: split stack overflow"); } if(argsize % sizeof(uintptr) != 0) { runtime·printf("runtime: stack split with misaligned argsize %d\n", argsize); runtime·throw("runtime: stack split argsize"); } minalloc = 0; reflectcall = framesize==1; if(reflectcall) { framesize = 0; // moreframesize_minalloc is only set in runtime·gc(), // that calls newstack via reflect·call(). minalloc = m->moreframesize_minalloc; m->moreframesize_minalloc = 0; if(framesize < minalloc) framesize = minalloc; } if(reflectcall && minalloc == 0 && m->morebuf.sp - sizeof(Stktop) - argsize - 32 > gp->stackguard) { // special case: called from reflect.call (framesize==1) // to call code with an arbitrary argument size, // and we have enough space on the current stack. // the new Stktop* is necessary to unwind, but // we don't need to create a new segment. top = (Stktop*)(m->morebuf.sp - sizeof(*top)); stk = (byte*)gp->stackguard - StackGuard; free = 0; } else { // allocate new segment. framesize += argsize; framesize += StackExtra; // room for more functions, Stktop. if(framesize < StackMin) framesize = StackMin; framesize += StackSystem; stk = runtime·stackalloc(framesize); top = (Stktop*)(stk+framesize-sizeof(*top)); free = framesize; } if(0) { runtime·printf("newstack framesize=%d argsize=%d morepc=%p moreargp=%p gobuf=%p, %p top=%p old=%p\n", framesize, argsize, m->morepc, m->moreargp, m->morebuf.pc, m->morebuf.sp, top, gp->stackbase); } top->stackbase = (byte*)gp->stackbase; top->stackguard = (byte*)gp->stackguard; top->gobuf = m->morebuf; top->argp = m->moreargp; top->argsize = argsize; top->free = free; m->moreargp = nil; m->morebuf.pc = nil; m->morebuf.sp = (uintptr)nil; // copy flag from panic top->panic = gp->ispanic; gp->ispanic = false; gp->stackbase = (uintptr)top; gp->stackguard = (uintptr)stk + StackGuard; sp = (byte*)top; if(argsize > 0) { sp -= argsize; dst = (uintptr*)sp; dstend = dst + argsize/sizeof(*dst); src = (uintptr*)top->argp; while(dst < dstend) *dst++ = *src++; } if(thechar == '5') { // caller would have saved its LR below args. sp -= sizeof(void*); *(void**)sp = nil; } // Continue as if lessstack had just called m->morepc // (the PC that decided to grow the stack). label.sp = (uintptr)sp; label.pc = (byte*)runtime·lessstack; label.g = m->curg; if(reflectcall) runtime·gogocallfn(&label, (FuncVal*)m->morepc); else runtime·gogocall(&label, m->morepc, m->cret); *(int32*)345 = 123; // never return }
// Called from reflect·call or from runtime·morestack when a new // stack segment is needed. Allocate a new stack big enough for // m->moreframesize bytes, copy m->moreargsize bytes to the new frame, // and then act as though runtime·lessstack called the function at // m->morepc. // 当需要一个新的栈段时调用,被reflect.call或者runtime.morestack调用 // 分配一个足够大的新栈,至少大于m->moreframesize。将m->moreargsize字节复制到新的栈帧。 // 然后伪装成在m->morepc处调用函数runtime.lessstack的样子。 void runtime·newstack(void) { int32 framesize, minalloc, argsize; Stktop *top; byte *stk, *sp; uintptr *src, *dst, *dstend; G *gp; Gobuf label; bool reflectcall; uintptr free; framesize = m->moreframesize; argsize = m->moreargsize; gp = m->curg; if(m->morebuf.sp < gp->stackguard - StackGuard) { //栈已经溢出 runtime·printf("runtime: split stack overflow: %p < %p\n", m->morebuf.sp, gp->stackguard - StackGuard); runtime·throw("runtime: split stack overflow"); } if(argsize % sizeof(uintptr) != 0) { //参数未对齐 runtime·printf("runtime: stack split with misaligned argsize %d\n", argsize); runtime·throw("runtime: stack split argsize"); } minalloc = 0; reflectcall = framesize==1; if(reflectcall) { framesize = 0; // moreframesize_minalloc is only set in runtime·gc(), // that calls newstack via reflect·call(). minalloc = m->moreframesize_minalloc; m->moreframesize_minalloc = 0; if(framesize < minalloc) framesize = minalloc; } if(reflectcall && minalloc == 0 && m->morebuf.sp - sizeof(Stktop) - argsize - 32 > gp->stackguard) { // special case: called from reflect.call (framesize==1) // to call code with an arbitrary argument size, // and we have enough space on the current stack. // the new Stktop* is necessary to unwind, but // we don't need to create a new segment. top = (Stktop*)(m->morebuf.sp - sizeof(*top)); stk = (byte*)gp->stackguard - StackGuard; free = 0; } else { // 分配新空间 // framesize大小,必须包含参数大小部分,为调用runtime.morestack保留部分,并且至少大于StackMin。然后还要加上StackSystem部分 framesize += argsize; framesize += StackExtra; // room for more functions, Stktop. if(framesize < StackMin) framesize = StackMin; framesize += StackSystem; stk = runtime·stackalloc(framesize); top = (Stktop*)(stk+framesize-sizeof(*top)); free = framesize; } if(0) { runtime·printf("newstack framesize=%d argsize=%d morepc=%p moreargp=%p gobuf=%p, %p top=%p old=%p\n", framesize, argsize, m->morepc, m->moreargp, m->morebuf.pc, m->morebuf.sp, top, gp->stackbase); } //将一些重要信息从结构体g中移到栈顶Stktop中 top->stackbase = (byte*)gp->stackbase; top->stackguard = (byte*)gp->stackguard; top->gobuf = m->morebuf; //只是借助m结构体临时传了一下参数,morebuf中记录的是栈空间不够的那个函数的pc,sp,g top->argp = m->moreargp; //参数 top->argsize = argsize; //参数大小 top->free = free; //可用空间大小 m->moreargp = nil; m->morebuf.pc = nil; m->morebuf.sp = (uintptr)nil; // copy flag from panic top->panic = gp->ispanic; gp->ispanic = false; gp->stackbase = (uintptr)top; gp->stackguard = (uintptr)stk + StackGuard; //每个goroutine的g->stackguard设置成指向栈底上面StackGuard的位置。 //装饰栈顶的参数区域 sp = (byte*)top; if(argsize > 0) { //将参数移过来 sp -= argsize; dst = (uintptr*)sp; dstend = dst + argsize/sizeof(*dst); src = (uintptr*)top->argp; while(dst < dstend) *dst++ = *src++; } if(thechar == '5') { //编译器标识 // caller would have saved its LR below args. sp -= sizeof(void*); *(void**)sp = nil; } // Continue as if lessstack had just called m->morepc // (the PC that decided to grow the stack). // 继续,伪装成好像是从m->morepc中调用lessstack函数的状态 // 跳转 label.sp = (uintptr)sp; label.pc = (byte*)runtime·lessstack; label.g = m->curg; if(reflectcall) runtime·gogocallfn(&label, (FuncVal*)m->morepc); else runtime·gogocall(&label, m->morepc, m->cret); //gogocall相当于一个直接的jmp,不是按函数协议跳转的 *(int32*)345 = 123; // never return }
// One round of scheduler: find a goroutine and run it. // The argument is the goroutine that was running before // schedule was called, or nil if this is the first call. // Never returns. static void schedule(G *gp) { int32 hz; schedlock(); if(gp != nil) { if(runtime·sched.predawn) runtime·throw("init rescheduling"); // Just finished running gp. gp->m = nil; runtime·sched.mcpu--; if(runtime·sched.mcpu < 0) runtime·throw("runtime·sched.mcpu < 0 in scheduler"); switch(gp->status){ case Grunnable: case Gdead: // Shouldn't have been running! runtime·throw("bad gp->status in sched"); case Grunning: gp->status = Grunnable; gput(gp); break; case Gmoribund: gp->status = Gdead; if(gp->lockedm) { gp->lockedm = nil; m->lockedg = nil; } gp->idlem = nil; unwindstack(gp, nil); gfput(gp); if(--runtime·sched.gcount == 0) runtime·exit(0); break; } if(gp->readyonstop){ gp->readyonstop = 0; readylocked(gp); } } // Find (or wait for) g to run. Unlocks runtime·sched. gp = nextgandunlock(); gp->readyonstop = 0; gp->status = Grunning; m->curg = gp; gp->m = m; // Check whether the profiler needs to be turned on or off. hz = runtime·sched.profilehz; if(m->profilehz != hz) runtime·resetcpuprofiler(hz); if(gp->sched.pc == (byte*)runtime·goexit) { // kickoff runtime·gogocall(&gp->sched, (void(*)(void))gp->entry); } runtime·gogo(&gp->sched, 0); }