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  阅读(662)  评论(0编辑  收藏  举报

导航