go语言调度器源代码情景分析之六:go汇编语言

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, $16-8
MOVQbuf+0(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, $16-8
......

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

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

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

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

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

原文地址:https://www.cnblogs.com/abozhang/p/10781682.html

时间: 2024-10-03 22:36:42

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

Linux内核源代码情景分析-强制性调度

Linux内核中进程的强制性调度,也就是非自愿的.被动的.剥夺式的调度,主要是由时间引起的.前面讲过这种调度发生在中断,异常,系统调用从系统空间返回用户空间的前夕,也就是在ret_with_reschedule可以看出,此时是否真的调用schedule(),最终还要取决于当前进程task_struct结构中的need_resched是否为1(非0),因此,问题就结为当前进程的need_resched是在什么情况下才置成1的.主要有如下几种情况: 1.在时钟中断的服务程序中,发现当前进程(连续)运

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

上个月,在花了一年半时间之后,写了55篇文章,分析完成了Chromium在Android上的实现,以及Android基于Chromium实现的WebView.学到了很多东西,不过也挺累的,平均不到两个星期一篇文章.本来想休息一段时间后,再继续分析Chromium使用的JS引擎V8.不过某天晚上,躺在床上睡不着,鬼使神差想着去创建一个个人站点,用来连载<Android系统源代码情景分析>一书的内容. 事情是这样的,躺在床上睡不着,就去申请了一个域名,0xcc0xcd.com.域名申请到了,总不能

Linux内核源代码情景分析-内存管理之用户页面的定期换出

我们已经看到在分配页面时,如果页面数不够,那么会调用page_launder,reclaim_page,__free_page将页面换出,并重新投入分配. 为了避免总是在CPU忙碌的时候,也就是在缺页异常发生的时候,临时再来搜寻可供换出的内存页面并加以换出,Linux内核定期地检查并且预先将若干页面换出,腾出空间,以减轻系统在缺页异常发生时的负担. 为此,在Linux内核中设置了一个专司定期将页面换出的"守护神"kswapd和kreclaimd. static int __init k

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

本文为原创,转载请注明:http://www.cnblogs.com/tolimit/ 引言 上期文章linux调度器源码分析 - 概述(一)已经把调度器相关的数据结构介绍了一遍,本篇着重通过代码说明调度器在系统启动初始化阶段是如何初始化和工作的.通过上期文章我们知道,在多核CPU和SMP系统中,每个CPU(多核COU中的每个核)都有自己的struct rq队列,而rq队列中又有着自己的struct cfs_rq和struct rt_rq.在初始化时就是对这三个结构进行初始化. init_tas

Linux内核源代码情景分析-内存管理之slab-回收

在上一篇文章Linux内核源代码情景分析-内存管理之slab-分配与释放,最后形成了如下图的结构: 图 1 我们看到空闲slab块占用的若干页面,不会自己释放:我们是通过kmem_cache_reap和kmem_cache_shrink来回收的.他们的区别是: 1.我们先看kmem_cache_shrink,代码如下: int kmem_cache_shrink(kmem_cache_t *cachep) { if (!cachep || in_interrupt() || !is_chaine

Linux源代码情景分析读书笔记 物理页面的分配

函数 alloc_pages流程图 Linux源代码情景分析读书笔记 物理页面的分配,布布扣,bubuko.com

Linux内核源代码情景分析-系统调用mknod

普通文件可以用open或者create创建,FIFO文件可以用pipe创建,mknod主要用于设备文件的创建. 在内核中,mknod是由sys_mknod实现的,代码如下: asmlinkage long sys_mknod(const char * filename, int mode, dev_t dev) //比如filename为/tmp/server_socket,dev是设备号 { int error = 0; char * tmp; struct dentry * dentry;

Linux内核源代码情景分析-文件系统的安装

执行sudo mount -t ext2 /dev/sdb1 /mnt/sdb,将文件系统挂在到/mnt/sdb上.系统调用mount,映射到内核层执行的是sys_mount.假设/dev/sdb1和/mnt/sdb都位于ext2文件系统中. asmlinkage long sys_mount(char * dev_name, char * dir_name, char * type, unsigned long flags, void * data)//dev_name指向了"/dev/sdb

Linux内核源代码情景分析-访问权限与文件安全性

在Linux内核源代码情景分析-从路径名到目标节点,一文中path_walk代码中,err = permission(inode, MAY_EXEC)当前进程是否可以访问这个节点,代码如下: int permission(struct inode * inode,int mask) { if (inode->i_op && inode->i_op->permission) { int retval; lock_kernel(); retval = inode->i_