goroutine代表一个调度单位
创建一个G的过程:
// Create a new g running fn with narg bytes of arguments starting
// at argp and returning nret bytes of results. callerpc is the
// address of the go statement that created this. The new g is put
// on the queue of g's waiting to run.
G*
runtime·newproc1(FuncVal *fn, byte *argp, int32 narg, int32 nret, void *callerpc)
{
byte *sp;
G *newg;
P *p;
int32 siz;
.......
p = g->m->p;
if((newg = gfget(p)) == nil) {
newg = runtime·malg(StackMin);
runtime·casgstatus(newg, Gidle, Gdead);
runtime·allgadd(newg); // publishes with a g->status of Gdead so GC scanner doesn't look at uninitialized stack.
}
if(newg->stack.hi == 0)
runtime·throw("newproc1: newg missing stack");
if(runtime·readgstatus(newg) != Gdead)
runtime·throw("newproc1: new g is not Gdead");
sp = (byte*)newg->stack.hi;
sp -= 4*sizeof(uintreg); // extra space in case of reads slightly beyond frame
sp -= siz;
runtime·memmove(sp, argp, narg);
if(thechar == '5') {
// caller's LR
sp -= sizeof(void*);
*(void**)sp = nil;
}
runtime·memclr((byte*)&newg->sched, sizeof newg->sched);
newg->sched.sp = (uintptr)sp;
newg->sched.pc = (uintptr)runtime·goexit + PCQuantum; // +PCQuantum so that previous instruction is in same function
newg->sched.g = newg;
runtime·gostartcallfn(&newg->sched, fn);
newg->gopc = (uintptr)callerpc;
runtime·casgstatus(newg, Gdead, Grunnable);
......
runqput(p, newg);
if(runtime·atomicload(&runtime·sched.npidle) != 0 && runtime·atomicload(&runtime·sched.nmspinning) == 0 && fn->fn != runtime·main) // TODO: fast atomic
wakep();
g->m->locks--;
if(g->m->locks == 0 && g->preempt) // restore the preemption request in case we've cleared it in newstack
g->stackguard0 = StackPreempt;
return newg;
}
- 从gfree list获取空闲的G
- 分配新的G和相应的stack
- 初始化G中的成员
- 将G放入P的运行队列中等待调度器来执行
- 唤醒空闲的P来运行新的G
G的终止:
// runtime·goexit continuation on g0.
static void
goexit0(G *gp)
{
runtime·casgstatus(gp, Grunning, Gdead);
gp->m = nil;
gp->lockedm = nil;
g->m->lockedg = nil;
gp->paniconfault = 0;
gp->defer = nil; // should be true already but just in case.
gp->panic = nil; // non-nil for Goexit during panic. points at stack-allocated data.
gp->writebuf.array = nil;
gp->writebuf.len = 0;
gp->writebuf.cap = 0;
gp->waitreason.str = nil;
gp->waitreason.len = 0;
gp->param = nil;
dropg();
if(g->m->locked & ~LockExternal) {
runtime·printf("invalid m->locked = %d
", g->m->locked);
runtime·throw("internal lockOSThread error");
}
g->m->locked = 0;
gfput(g->m->p, gp);
schedule();
}
- 清空G的成员
- 从M中分离G
- 将G放入gfree list中
- 调度执行其它的G
goroutine的stack
普通的G的初始stack大小为2048,在每个函数被调用之前,都会检查stack是否会越界,如果会越界,则首先要增加G的stack.
在GC的过程中会检查G的stack是否需要收缩.