// func mcall(fn func(*g))
// Switch to m->g0's stack, call fn(g).
// Fn must never return. It should gogo(&g->sched)
// to keep running g.
// 上面的翻译:
// 切换到 m->g0 的栈上,然后调用 fn (g)函数。
// fn 必须不能返回。fn 应该 调用 gogo 函数 来保持运行 g。
TEXT runtime·mcall(SB), NOSPLIT, $0-8
    MOVQ    fn+0(FP), DI       // 很容易理解的,将fn这个函数指针存到DI寄存器。
    get_tls(CX)                     // 宏,顾名思义,tls 是线程局部存储,将这个存储的开始位置存到 CX
    MOVQ    g(CX), AX   // save state in g->sched     // g(CX)还是宏,在tls上可以拿到g,也就是当前正在跑的g,把这个g的地址存到AX寄存器
    MOVQ    0(SP), BX   // caller's PC                       // 这一句有点难懂,得明白整个golang的函数调用规则才能明白。简介:golang的函数栈的大小,不包含传入的参数和返回值,这两个部分由调用者管理,mcall例子中,fn是传入参数,这个fn是放在【调用mcall的函数】的函数栈里的。mcall除了一个传入参数fn之外,没有其他变量了,所以mcall的栈大小就是0。一个栈大小为0的函数,栈顶,也就是SP指向的位置,刚好就是存的调用mcall的函数的下一条指令,也就是这里所谓的 caller 的 PC。这个PC值为什么会出现在这里,那就是因为,这是CALL指令自动做的事情,也是golang编译器做的事情,调用某函数时,先把本函数的下一掉指令push进栈。话说回来了,既然mcall不需要额外的栈大小了,前面说了,栈大小是0,所以,自然而然的,SP,存的东西就是caller的PC。好,拿到了caller‘s PC,把他存到BX里。
    MOVQ    BX, (g_sched+gobuf_pc)(AX) // 这句简单,AX存了g的地址,BX存了caller's PC,那这句就是把caller's PC 存到 g 结构体的 sched 的 gobuf 的 pc 字段。
    LEAQ    fn+0(FP), BX    // caller's SP // 这句又难了。为什么栈上,存fn的地方,是caller的栈顶呢。根据上面的解释能猜到一点,因为栈上存fn这个事,就是由caller来处理的,caller的栈在往下生长的时候,最后一个数据项就是fn。再下面就是caller pc,等等等等。
    MOVQ    BX, (g_sched+gobuf_sp)(AX) // BX存的是caller的SP,将BX存到g的相应位置 
    MOVQ    AX, (g_sched+gobuf_g)(AX)   // AX存的就是g本身,也存起来
    MOVQ    BP, (g_sched+gobuf_bp)(AX) //  这一句不太重要,好像BP寄存器已经没啥用了。可能在debug的时候,有点用??不清楚,这个,但是肯定是跟切换上下文的主逻辑和函数调用的主逻辑没什么关系了。
    // switch to m->g0 & its stack, call fn // 切换到m->g0的栈,然后调用fn
    MOVQ    g(CX), BX // 把当前g的地址存到BX里
    MOVQ    g_m(BX), BX // 根据g,可以拿到m
    MOVQ    m_g0(BX), SI // 根据m,又可以拿到 g0,目的就是拿到 g0,下面要切换了。
    CMPQ    SI, AX  // if g == m->g0 call badmcall // 如果g0就是g,那不行,这种情况,我们不考虑先。
    JNE 3(PC) // 假如g!= g0,就跳到红色标记,继续执行。 
    MOVQ    $runtime·badmcall(SB), AX
    JMP AX
    红色标记:MOVQ    SI, g(CX)   // g = m->g0 // SI存的是g0,将g0变成当前g。
    MOVQ    (g_sched+gobuf_sp)(SI), SP  // sp = m->g0->sched.sp // 将g0的SP值,拿出来,交给寄存器SP,看见没,开始切换了,或者说,已经切完了,下面就是调用 fn (g)
    PUSHQ   AX // AX 是什么,存的就是刚才的g,不是现在的g0,将g放到栈上。这一步就是普通的,我要调用fn函数了,我要把参数g,先放到栈上。
    MOVQ    DI, DX // 这一步把fn存到DX不知道要干嘛,可能后续调用fn的时候,会用到??不知道,等再接着看。
    MOVQ    0(DI), DI // 这一步和下一步,就是调用 fn
    CALL    DI           // 调用fn
    POPQ    AX
    MOVQ    $runtime·badmcall2(SB), AX
    JMP AX
    RET

golang mcall的更多相关文章

  1. Golang网络库中socket阻塞调度源码剖析

    本文分析了Golang的socket文件描述符和goroutine阻塞调度的原理.代码中大部分是Go代码,小部分是汇编代码.完整理解本文需要Go语言知识,并且用Golang写过网络程序.更重要的是,需 ...

  2. Golang源码探索(二) 协程的实现原理

    Golang最大的特色可以说是协程(goroutine)了, 协程让本来很复杂的异步编程变得简单, 让程序员不再需要面对回调地狱, 虽然现在引入了协程的语言越来越多, 但go中的协程仍然是实现的是最彻 ...

  3. Golang源码探索(二) 协程的实现原理(转)

    Golang最大的特色可以说是协程(goroutine)了, 协程让本来很复杂的异步编程变得简单, 让程序员不再需要面对回调地狱,虽然现在引入了协程的语言越来越多, 但go中的协程仍然是实现的是最彻底 ...

  4. Golang源码学习:调度逻辑(四)系统调用

    Linux系统调用 概念:系统调用为用户态进程提供了硬件的抽象接口.并且是用户空间访问内核的唯一手段,除异常和陷入外,它们是内核唯一的合法入口.保证系统的安全和稳定. 调用号:在Linux中,每个系统 ...

  5. Golang源码学习:监控线程

    监控线程是在runtime.main执行的时候在系统栈中创建的,监控线程与普通的工作线程区别在于,监控线程不需要绑定p来运行. 监控线程的创建与启动 简单的调用图 先给出个简单的调用图,好心里有数,逐 ...

  6. 排查golang的性能问题 go pprof 实践

    小结: 1.内存消耗分析 list peek  定位到函数   https://mp.weixin.qq.com/s/_LovnIqJYAuDpTm2QmUgrA 使用pprof和go-torch排查 ...

  7. Golang, 以17个简短代码片段,切底弄懂 channel 基础

    (原创出处为本博客:http://www.cnblogs.com/linguanh/) 前序: 因为打算自己搞个基于Golang的IM服务器,所以复习了下之前一直没怎么使用的协程.管道等高并发编程知识 ...

  8. 说说Golang的使用心得

    13年上半年接触了Golang,对Golang十分喜爱.现在是2015年,离春节还有几天,从开始学习到现在的一年半时间里,前前后后也用Golang写了些代码,其中包括业余时间的,也有产品项目中的.一直 ...

  9. TODO:Golang指针使用注意事项

    TODO:Golang指针使用注意事项 先来看简单的例子1: 输出: 1 1 例子2: 输出: 1 3 例子1是使用值传递,Add方法不会做任何改变:例子2是使用指针传递,会改变地址,从而改变地址. ...

随机推荐

  1. jQuary总结11:jQuery插件封装---jQuery封装 手风琴 动画插件

    完整代码下载点击我的GitHub: https://github.com/XingJYGo/jquery-accordion 1 手风琴的效果展示如下: 2 封装插件目录结构如下: 主要包括:HTML ...

  2. Ubuntu使用tzselect修改时区

    1.命令行运行 sudo tzselect 2.选择洲区(这里选择亚洲Asia) waichung@desktop:~$ sudo tzselect [sudo] password for waich ...

  3. eclipse使用配置

    eclipse设置工作空间为UTF-8(适应中文,避免乱码) windows---preference---general---workspace,修改textfile encoding eclips ...

  4. XJOI 3601 技能(贪心+二分)

    题目描述: 有一个oier,他有n个算法技能,每个技能有一个水平值,每个技能的水平上限都是A,设这个oier有cnt个技能达到了A, 设所有水平值的最小值为mi,那么这个oier的战斗力为cnt×Cf ...

  5. android之实现底部TabHost

    先说布局文件,如下:利用android:layout_alignParentBottom="true" 实现底部显示 <?xml version="1.0" ...

  6. Spring Boot - 杂项

    可以使用devtools功能来实现热部署(Hot Swapping),需要加入依赖(如maven):spring-boot-devtools 可以实现修改代码并保存后的自动编译.重启 依赖于Eclip ...

  7. MVVM Caliburn.Micro学习记录

    wpf中MVVM一直用的自己写的框架,最近试了试Caliburn.Micro. Caliburn.Micro可以通过x:name来进行属性和事件绑定. 比如 <Button x:Name=&qu ...

  8. sqlserver 事务日志已满解决方案

    sqlserver 事务日志已满解决方案 可参考这篇博客: https://www.cnblogs.com/strayromeo/p/6961758.html 一.删除日志文件:(不建议) 二.手动收 ...

  9. “全栈2019”Java第八十四章:接口中嵌套接口详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  10. IO多路复用,协程,

    一.单线程的并发 import socket import select client1 = socket.socket() client1.setblocking(False) # 百度创建连接: ...