go的协程

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\n", 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是否需要收缩.

posted on 2015-05-17 14:28  richmonkey  阅读(680)  评论(0)    收藏  举报

导航