• 从gcc代码看go语言goroutine和channel实现


    一、go语法解析主要文件

    go语言的前端解释代码位于gcc-4.8.2gccgogofrontendparse.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.2gccgogofrontend untime.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.2libgo untimeproc.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.2libgogouiltinuiltin.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.2gccgogofrontendgogo.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.2gccgogofrontendgogo-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);
    }
    }

  • 相关阅读:
    kettle excel input 利用通配符一次读入多份文件
    PowerDesigner Name、Code 映射设置
    PowerDesigner 建立约束
    PowerDesigner 创建概念模型、转换显示风格、概念模型转逻辑模型
    SQL 语言分类
    PowerDesigner 使用域、逻辑模型转物理模型、查看DDL语句
    DB、ETL、DW、OLAP、DM、BI关系结构图
    读懂BI商业智能与大数据应用的区别
    java 生成excel
    IntelliJ IDEA 2016 2.5 安装 并使用其新建一个maven web项目部署发布
  • 原文地址:https://www.cnblogs.com/tsecer/p/11460552.html
Copyright © 2020-2023  润新知