从gcc代码看go语言goroutine和channel实现
一、go语法解析主要文件
go语言的前端解释代码位于gcc-4.8.2\gcc\go\gofrontend\parse.cc文件,对于源文件的解析从Parse::program开始。从这个函数看,源文件开始必须通过package指明自己的名称;如果有import,它们必须击中在package后面,第一个声明之前;从最高层级上看,整个源文件全部由声明组成,这些声明包括const、type、var、func四种。以典型的helloworld为例,其内容为
package main
import "fmt"
func main() {
fmt.Println("Hello, 世界")
}
由开始的package、接下来的import以及最后的func声明组成。
二、go的语法实现
1、将go转换为对运行时库__go_go的调用
gcc-4.8.2\gcc\go\gofrontend\runtime.def
// Start a new goroutine.
DEF_GO_RUNTIME(GO, "__go_go", P2(FUNC_PTR, POINTER), R0())
void
Parse::go_or_defer_stat()
{
……
Statement* stat;
if (is_go)
stat = Statement::make_go_statement(call_expr, stat_location);
else
stat = Statement::make_defer_statement(call_expr, stat_location);
this->gogo_->add_statement(stat);
this->gogo_->add_block(this->gogo_->finish_block(stat_location),
stat_location);
}
Statement*
Statement::make_go_statement(Call_expression* call, Location location)
{
return new Go_statement(call, location);
}
Bstatement*
Go_statement::do_get_backend(Translate_context* context)
{
Expression* fn;
Expression* arg;
if (!this->get_fn_and_arg(&fn, &arg))
return context->backend()->error_statement();
Expression* call = Runtime::make_call(Runtime::GO, this->location(), 2,
fn, arg);
tree call_tree = call->get_tree(context);
Bexpression* call_bexpr = tree_to_expr(call_tree);
return context->backend()->expression_statement(call_bexpr);
}
2、运行时库中__go_go的实现
从实现上看,是将这个goroutine表达为一个结构然后通过runqput函数添加到一个运行时队列中,而底层是通过glibc内置的context切换实现。
gcc-4.8.2\libgo\runtime\proc.c
G*
__go_go(void (*fn)(void*), void* arg)
{
……
// Avoid warnings about variables clobbered by
// longjmp.
byte * volatile vsp = sp;
size_t volatile vspsize = spsize;
G * volatile vnewg = newg;
getcontext(&vnewg->context);
vnewg->context.uc_stack.ss_sp = vsp;
#ifdef MAKECONTEXT_STACK_TOP
vnewg->context.uc_stack.ss_sp += vspsize;
#endif
vnewg->context.uc_stack.ss_size = vspsize;
makecontext(&vnewg->context, kickoff, 0);
runqput(m->p, vnewg);
if(runtime_atomicload(&runtime_sched.npidle) != 0 && runtime_atomicload(&runtime_sched.nmspinning) == 0 && fn != runtime_main) // TODO: fast atomic
wakep();
m->locks--;
return vnewg;
}
3、goroutine的调度
有下面的文章参考https://tonybai.com/2017/06/23/an-intro-about-goroutine-scheduler/:大致来说,相当于在操作系统基础上虚拟自己的调度线程并将goroutine在不同的procesor中分配调度
三、channel实现
1、channel的创建
Expression*
Builtin_call_expression::do_lower(Gogo* gogo, Named_object* function,
Statement_inserter* inserter, int)
{
……
case BUILTIN_MAKE:
return this->lower_make();
……
}
Expression*
Builtin_call_expression::lower_make()
{
……
call = Runtime::make_call((have_big_args
? Runtime::MAKEMAPBIG
: Runtime::MAKEMAP),
loc, 2, type_arg, len_arg);
……
}
// The garbage collector is assuming that Hchan can only contain pointers into the stack
// and cannot contain pointers into the heap.
struct Hchan
{
uintgo qcount; // total data in the q
uintgo dataqsiz; // size of the circular q
uint16 elemsize;
bool closed;
uint8 elemalign;
uintgo sendx; // send index
uintgo recvx; // receive index
WaitQ recvq; // list of recv waiters
WaitQ sendq; // list of send waiters
Lock;
};
2、send的运行时
Bstatement*
Send_statement::do_get_backend(Translate_context* context)
{
……
else if (can_take_address)
{
// Must pass address of value. The function doesn't change the
// value, so just take its address directly.
code = Runtime::SEND_BIG;
val = Expression::make_unary(OPERATOR_AND, val, loc);
}
……
Expression* call = Runtime::make_call(code, loc, 3, td, this->channel_, val);
……
}
可以看到,其中并没有对发送数据再自己拷贝一份,只是传递了数据指针,
void
runtime_chansend(ChanType *t, Hchan *c, byte *ep, bool *pres, void *pc)
{
……
mysg.elem = ep;
mysg.g = g;
mysg.selgen = NOSELGEN;
g->param = nil;
enqueue(&c->sendq, &mysg);
runtime_park(runtime_unlock, c, "chan send");
……
}
四、一些内置保留字
gcc-4.8.2\libgo\go\builtin\builtin.go
func make(Type, size IntegerType) Type
// The new built-in function allocates memory. The first argument is a type,
// not a value, and the value returned is a pointer to a newly
// allocated zero value of that type.
func new(Type) *Type
// The complex built-in function constructs a complex value from two
// floating-point values. The real and imaginary parts must be of the same
// size, either float32 or float64 (or assignable to them), and the return
// value will be the corresponding complex type (complex64 for float32,
// complex128 for float64).
func complex(r, i FloatType) ComplexType
// The real built-in function returns the real part of the complex number c.
// The return value will be floating point type corresponding to the type of c.
func real(c ComplexType) FloatType
gcc-4.8.2\gcc\go\gofrontend\gogo.cc
Function_type* complex_type = Type::make_function_type(NULL, NULL, NULL, loc);
complex_type->set_is_varargs();
complex_type->set_is_builtin();
this->globals_->add_function_declaration("complex", NULL, complex_type, loc);
gcc-4.8.2\gcc\go\gofrontend\gogo-tree.cc
tree
Gogo::allocate_memory(Type* type, tree size, Location location)
{
// If the package imports unsafe, then it may play games with
// pointers that look like integers.
if (this->imported_unsafe_ || type->has_pointer())
{
static tree new_fndecl;
return Gogo::call_builtin(&new_fndecl,
location,
"__go_new",
1,
ptr_type_node,
sizetype,
size);
}
else
{
static tree new_nopointers_fndecl;
return Gogo::call_builtin(&new_nopointers_fndecl,
location,
"__go_new_nopointers",
1,
ptr_type_node,
sizetype,
size);
}
}