goroutine的实现原理

Time: 一月 30, 2013
Category: Compiler and vm

goroutine的实现主要依赖下面这几个API,linux386平台的实现在runtime/asm_386.s文件里。这里只显示API
[c]// void gosave(Gobuf*)

// void gogo(Gobuf*, uintptr)
// restore state from Gobuf; longjmp

// void gogocall(Gobuf*, void (*fn)(void))
// restore state from Gobuf but then call fn.
// (call fn, returning to state in Gobuf)

// void mcall(void (*fn)(G*))
// Switch to m->g0's stack, call fn(g).
// Fn must never return. It should gogo(&g->sched)
// to keep running g.[/c]
而调度的实现是在runtime/proc.c文件里。

gosave类似于setjmp,但它也保存了当前正在运行的G的状态,PC和SP,gogo类似于longjmp,恢复G的SP和PC。SP和PC是通过一个Gobuf的结构来维护的。gogocall和gogo的作用差不多,但跳转到相应的G之后,会继续执行一个fn函数。mcall稍微复杂点,但也和googcall类似。

通常从M跳转到G的执行,是使用gogo,而从G跳转到M,是使用mcall。这是因为在M的栈上运行的只有一个mstart()函数,而在mstart()函数内主要的工作就是初始化一些数据,然后最后触发调度器schedule(),所以如果是使用gogo从G返回到M,那么M就会执行完毕。

整个过程就是通过一个全局的Sched结构来维护所有的调度状态。比libtask的实现复杂点,和xv6的proc实现也差不多。

Leave a Comment