// 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. 编写高质量代码改善C#程序的157个建议——建议69:应使用finally避免资源泄漏

    建议69:应使用finally避免资源泄漏 除非发生让应用程序中断的异常,否则finally总是会先于return执行.finally的这个语言特性决定了资源释放的最佳位置就是在finally块中:另 ...

  2. Hadoop各个启动流

    (0)如果集群是第一次启动,需要格式化namenodehadoop@node1:~$ hdfs namenode –format (1)启动HDFS:hadoop@master:~$ start-df ...

  3. vim的基本使用

    Vim 编辑器中设置了三种模式—命令模式.末行模式和编辑模式,每种模式分别又支持多种不同的命令快捷键,这大大提高了工作效率,而且用户在习惯之后也会觉得相当顺手.要想高效率地操作文本,就必须先搞清这三种 ...

  4. 在Android模拟器里安装apk

    [原文]http://Android.tgbus.com/android/tutorial/201104/349532.shtml 1.运行SDK Manager,选择模拟器,并运行模拟器. 2.将需 ...

  5. [LeetCode 题解]: Longest Common Prefix

    Write a function to find the longest common prefix string amongst an array of strings. 题解: 寻找一组字符串的最 ...

  6. [LeetCode 题解]: Generate Parentheses

    Given n pairs of parentheses, write a function to generate all combinations of well-formed parenthes ...

  7. GridView中合并单元格

    using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Da ...

  8. C# usb设备的自动退出

    using System; using System.Collections.Generic; using System.Text; using System.Runtime.InteropServi ...

  9. AJAX方式调用百度天气

    后台代码: [HttpPost] public string AjaxWeather() { string CityName = string.IsNullOrEmpty(Request.Form[& ...

  10. python之爬虫--番外篇(一)进程,线程的初步了解

    整理这番外篇的原因是希望能够让爬虫的朋友更加理解这块内容,因为爬虫爬取数据可能很简单,但是如何高效持久的爬,利用进程,线程,以及异步IO,其实很多人和我一样,故整理此系列番外篇 一.进程 程序并不能单 ...