go语言runtime(包括调度器)源代码中有部分代码是用汇编语言编写的,不过这些汇编代码并非针对特定体系结构的汇编代码,而是go语言引入的一种伪汇编,它同样也需要经过汇编器转换成机器指令才能被CPU执行。需要注意的是,用go汇编语言编写的代码一旦经过汇编器转换成机器指令之后,再用调试工具反汇编出来的代码已经不是go语言汇编代码了,而是跟平台相关的汇编代码。

go汇编格式跟前面讨论过的AT&T汇编基本上差不多,但也有些重要区别,本节就这些差异做一个简单说明。

寄存器

go汇编语言中使用的寄存器的名字与AMD64不太一样,下表显示了它们之间的对应关系:

除了这些跟AMD64 CPU硬件寄存器一一对应的寄存器外,go汇编还引入了几个没有任何硬件寄存器与之对应的虚拟寄存器,这些寄存器一般用来存放内存地址,引入它们的主要目的是为了方便程序员和编译器用来定位内存中的代码和数据。

下面重点介绍在go汇编中常见的2个虚拟寄存器的使用方法:

FP虚拟寄存器:主要用来引用函数参数。go语言规定函数调用时参数都必须放在栈上,比如被调用函数使用 first_arg+0(FP) 来引用调用者传递进来的第一个参数,用second_arg+8(FP)来引用第二个参数 ,以此类推,这里的first_arg和second_arg仅仅是一个帮助我们阅读源代码的符号,对编译器来说无实际意义,+0和+8表示相对于FP寄存器的偏移量。我们用一个runtime中的函数片段作为例子来看看FP的使用。

go runtime中有一个叫gogo的函数,它接受一个gobuf类型的指针

// func gogo(buf *gobuf)
// restore state from Gobuf; longjmp
TEXT runtime·gogo(SB), NOSPLIT, $-
MOVQbuf+(FP), BX// gobuf -->bx
......

MOVQ buf+0(FP), BX 这一条指令把调用者传递进来的指针buf放入BX寄存器中,可以看到,在gogo函数是通过buf+0(FP)这种方式获取到参数的。从被调用函数(此处为gogo函数)的角度来看,FP与函数栈帧之间的关系如下图,可以看出FP寄存器指向调用者的栈帧,而不是被调用函数的栈帧。

SB虚拟寄存器:保存程序地址空间的起始地址。还记得在函数调用栈一节我们看过的进程在内存中的布局那张图吗,这个SB寄存器保存的值就是代码区的起始地址,它主要用来定位全局符号。go汇编中的函数定义、函数调用、全局变量定义以及对其引用会用到这个SB虚拟寄存器。对于这个虚拟寄存器,我们不用过多的关注,在代码中看到它时知道它是一个虚拟寄存器就行了。

操作码

AT&T格式的寄存器操作码一般使用小写且寄存器的名字前面有个%符号,而go汇编使用大写而且寄存器名字前没有%符号,比如:

#AT&T格式
mov %rbp,%rsp #go汇编格式
MOVQ BP,SP

操作数宽度(即操作数的位数)

AT&T格式的汇编指令中如果有寄存器操作数,则根据寄存器的名字(比如rax, eax, ax, al分别代表64,32,16和8位寄存器)就可以确定操作数到底是多少位(8,16,32还是64位),所以不需要操作码后缀,如果没有寄存器操作数又是访存指令的话,则操作码需要加上后缀b、w、l或q来指定到底存取内存中的多少个字节。

而go汇编中,寄存器的名字没有位数之分,比如AX寄存器没有什么RAX, EAX之类的名字,指令中一律只能使用AX。所以如果指令中有操作数寄存器或是指令需要访问内存,则操作码都需要带上后缀B(8位)、W(16位)、D(32位)或Q(64位)

函数定义

还是以go runtime中的gogo函数为例:

// func gogo(buf *gobuf)
// restore state from Gobuf; longjmp
TEXT runtime·gogo(SB), NOSPLIT, $-
......

下面对这个函数定义的第一行的各部分做个说明:

TEXT runtime·gogo(SB):指明在代码区定义了一个名字叫gogo的全局函数(符号),该函数属于runtime包。

NOSPLIT:指示编译器不要在这个函数中插入检查栈是否溢出的代码。

$16-8:数字16说明此函数的栈帧大小为16字节,8说明此函数的参数和返回值一共需要占用8字节内存。因为这里的gogo函数没有返回值,只有一个指针参数,对于AMD64平台来说指针就是8字节。go语言中函数调用的参数和函数返回值都是放在栈上的,而且这部分栈内存是由调用者而非被调用函数负责预留,所以在函数定义时需要说明到底需要在调用者的栈帧中预留多少空间。

go汇编还有一些用法比较特别的地方,现在不讨论,等我们分析源代码遇到它们时再结合上下文做详细说明。

go语言调度器源代码情景分析之六:go汇编语言的更多相关文章

  1. go语言调度器源代码情景分析之五:汇编指令

    本文是<go调度器源代码情景分析>系列 第一章 预备知识的第4小节. 汇编语言是每位后端程序员都应该掌握的一门语言,因为学会了汇编语言,不管是对我们调试程序还是研究与理解计算机底层的一些运 ...

  2. go语言调度器源代码情景分析之四:函数调用栈

    本文是<go调度器源代码情景分析>系列 第一章 预备知识的第3小节. 什么是栈 栈是一种“后进先出”的数据结构,它相当于一个容器,当需要往容器里面添加元素时只能放在最上面的一个元素之上,需 ...

  3. go语言调度器源代码情景分析之三:内存

    本文是<go调度器源代码情景分析>系列 第一章 预备知识的第2小节. 内存是计算机系统的存储设备,其主要作用是协助CPU在执行程序时存储数据和指令. 内存由大量内存单元组成,内存单元大小为 ...

  4. go语言调度器源代码情景分析之二:CPU寄存器

    本文是<go调度器源代码情景分析>系列 第一章 预备知识的第1小节. 寄存器是CPU内部的存储单元,用于存放从内存读取而来的数据(包括指令)和CPU运算的中间结果,之所以要使用寄存器来临时 ...

  5. go语言调度器源代码情景分析之一:开篇语

    专题简介 本专题以精心设计的情景为线索,结合go语言最新1.12版源代码深入细致的分析了goroutine调度器实现原理. 适宜读者 go语言开发人员 对线程调度器工作原理感兴趣的工程师 对计算机底层 ...

  6. 《Android系统源代码情景分析》连载回忆录:灵感之源

    上个月,在花了一年半时间之后,写了55篇文章,分析完成了Chromium在Android上的实现,以及Android基于Chromium实现的WebView.学到了很多东西,不过也挺累的,平均不到两个 ...

  7. linux调度器源码分析 - 初始化(二)

    本文为原创,转载请注明:http://www.cnblogs.com/tolimit/ 引言 上期文章linux调度器源码分析 - 概述(一)已经把调度器相关的数据结构介绍了一遍,本篇着重通过代码说明 ...

  8. [翻译] 深入浅出Go语言调度器:第一部分 - 系统调度器

    目录 译者序 序 介绍 系统调度器 执行指令 Figure 1 Listing 1 Listing 2 Listing 3 线程状态 任务侧重 上下文切换 少即是多 寻找平衡 缓存行 Figure 2 ...

  9. Linux内核源代码情景分析系列

    http://blog.sina.com.cn/s/blog_6b94d5680101vfqv.html Linux内核源代码情景分析---第五章 文件系统  5.1 概述 构成一个操作系统最重要的就 ...

随机推荐

  1. 在网页中使用particlesjs实现背景的动态粒子特效

    先上一张效果图: 这种动态的背景特效,制作起来其实非常简单. 使用了particles.js particles.js可以从github网站下载到最新的源码,网址是 https://github.co ...

  2. Surface pro 4 使用心得

    今天谈谈这几个月Surface pro 4的使用心得.这篇后面有点跑题,行文也比较随意,就当闲笔了. 设备简述 使用体验 优点 不足 优雅使用 系统界面 应用 系统应用 工具应用 生产工具 其他应用 ...

  3. Spring使用 --- 基本概念(一):DI,依赖注入

    Table of Contents 什么是依赖注入 怎样使用 什么时候使用 好处 本文讲述sprint基本概念之一: DI, 即依赖注入. 什么是依赖注入 说类A依赖于类B,最简单的例子是类A有一个类 ...

  4. 洛谷 P2205 解题报告

    P2205 画栅栏Painting the Fence 题目描述 \(Farmer\) \(John\) 想出了一个给牛棚旁的长围墙涂色的好方法.(为了简单起见,我们把围墙看做一维的数轴,每一个单位长 ...

  5. 关于HTML、CSS、JavaScript三者关系的简述

    总述 我对于网页这部分的理解吧,对于静态网页来说,无非分为三部分:第一部分.HTML,第二部分.CSS,第三部分.JavaScript(这部分暂且放一边).这俩个给我的第一印象就是,这你妹的都是一串串 ...

  6. Selenium2Lib库之键盘常用关键字实战

    Press Key关键字 按F5 查看Press Key关键字的说明,如下图: Press Key关键字是用于通过键盘模拟由定位器确定的元素的用户按键.‘值’是单个字符,字符串或数值的ASCII码的“ ...

  7. TX2 安装 ROS 依赖库错误解决办法

    一.更换ubuntu 16.04 更新源 1. 更新源 deb http://mirrors.ustc.edu.cn/ubuntu-ports/ xenial main multiverse rest ...

  8. 使用crypto-js对数据进行AES加密、解密

    前段时间做项目有用到数据加密,前端加密,后端解密(前端也可以解密),话不多说进入正题: 第一步: npm i crypto-js -S 第二步: 在需要加密或解密的地方引入crypto-js: imp ...

  9. java 保留字段volatile、transient、native、synchronized

    1.volatile Java语言提供了一种稍弱的同步机制,即volatile变量,用来确保将变量的更新操作通知到其他线程.当把变量声明为volatile类型后,编译器与运行时都会注意到这个变量是共享 ...

  10. wireshark使用方法

    抓取报文: 下载和安装好Wireshark之后,启动Wireshark并且在接口列表中选择接口名,然后开始在此接口上抓包.例如,如果想要在无线网络上抓取流量,点击无线接口.点击Capture Opti ...