本文是《go调度器源代码情景分析》系列 第一章 预备知识的第1小节。

寄存器是CPU内部的存储单元,用于存放从内存读取而来的数据(包括指令)和CPU运算的中间结果,之所以要使用寄存器来临时存放数据而不是直接操作内存,一是因为CPU的工作原理决定了有些操作运算只能在CPU内部进行,二是因为CPU读写寄存器的速度比读写内存的速度快得多。

为了便于交流和使用汇编语言进行编程,CPU厂商为每个寄存器都取了一个名字,比如AMD64 CPU中的rax, rbx, rcx, rdx等等,这样程序员就可以很方便的在汇编代码中使用寄存器的名字来进行编程,为了对寄存器的使用有个直观的感受,我们用个例子来简单的说明一下。

假设有如下go语言编写的一行代码:

c = a + b

在AMD64 Linux平台下,使用go编译器编译它可得到如下AT&T格式的汇编代码(如果对汇编代码不熟悉的话可以直接看每一条指令后面的注释,不影响我们理解):

mov (%rsp),%rdx     #把变量a的值从内存中读取到寄存器rdx中
mov 0x8(%rsp),%rax #把变量b的值从内存中读取到寄存器rax中
add %rdx,%rax #把寄存器rdx和rax中的值相加,并把结果放回rax寄存器中
mov %rax,0x10(%rsp) #把寄存器rax中的值写回变量c所在的内存

可以看到,上面的一行go语言代码被编译成了4条汇编指令,指令中出现的rax,rdx和rsp都是寄存器的名字(AT&T格式的汇编代码中所有寄存器名字前面都有一个%符号),虽然这里只有4条指令,但也从一个侧面说明汇编代码其实比较简单,它所做的工作不外乎就是把数据在内存和寄存器中搬来搬去或做一些基础的数学和逻辑运算。

不同体系结构的CPU,其内部寄存器的数量、种类以及名称可能大不相同,这里我们只介绍目前使用最为广泛的AMD64这种体系结构的CPU,这种CPU共有20多个可以直接在汇编代码中使用的寄存器,其中有几个寄存器在操作系统代码中才会见到,而应用层代码一般只会用到如下分为三类的19个寄存器。

  • 通用寄存器:rax, rbx, rcx, rdx, rsi, rdi, rbp, rsp, r8, r9, r10, r11, r12, r13, r14, r15寄存器。CPU对这16个通用寄存器的用途没有做特殊规定,程序员和编译器可以自定义其用途(下面会介绍,rsp/rbp寄存器其实是有特殊用途的);
  • 程序计数寄存器(PC寄存器,有时也叫IP寄存器):rip寄存器。它用来存放下一条即将执行的指令的地址,这个寄存器决定了程序的执行流程;
  • 段寄存器:fs和gs寄存器。一般用它来实现线程本地存储(TLS),比如AMD64 linux平台下go语言和pthread都使用fs寄存器来实现系统线程的TLS,在本章线程本地存储一节和第二章详细分析goroutine调度器的时候我们可以分别看到Linux平台下Pthread线程库和go是如何使用fs寄存器的。

上述这些寄存器除了fs和gs段寄存器是16位的,其它都是64位的,也就是8个字节,其中的16个通用寄存器还可以作为32/16/8位寄存器使用,只是使用时需要换一个名字,比如可以用eax这个名字来表示一个32位的寄存器,它使用的是rax寄存器的低32位。

为了便于查阅,下表列出这些64通用寄存器对应的32/16/8位寄存器的名字:

通用寄存器的用法如前面我们所见,主要用于临时存放数据,后面的章节我们还会见到大量使用通用寄存器的例子,所以这里就不对其进行详细介绍了,但有三个比较特殊的寄存器值得在这里单独提出来做一下说明:

  • rip寄存器

rip寄存器里面存放的是CPU即将执行的下一条指令在内存中的地址。看如下汇编语言代码片段:

0x0000000000400770:    add %rdx,%rax
0x0000000000400773: mov $0x0,%ecx

假设当前CPU正在执行第一条指令,这条指令在内存中的地址是0x0000000000400770,紧接它后面的下一条指令的地址是0x0000000000400773,所以此时rip寄存器里面存放的值是0x0000000000400773。

这里需要牢记的就是rip寄存器的值不是正在被CPU执行的指令在内存中的地址,而是紧挨这条正在被执行的指令后面那一条指令的地址。

读者可能会有疑问,在前面的两个汇编指令片段中并没有指令修改rip寄存器的值,是怎么做到让它一直指向下一条即将执行的指令的呢?其实修改rip寄存器的值是CPU自动控制的,不需要我们用指令去修改,当然CPU也提供了几条可以间接修改rip寄存器的指令,在汇编语言一节中我们会详细介绍CPU自动修改以及用指令修改rip寄存器值的两种方式。

  • rsp 栈顶寄存器和rbp栈基址寄存器

这两个寄存器都跟函数调用栈有关,其中rsp寄存器一般用来存放函数调用栈的栈顶地址,而rbp寄存器通常用来存放函数的栈帧起始地址,编译器一般使用这两个寄存器加一定偏移的方式来访问函数局部变量或函数参数,比如:

mov 0x8(%rsp),%rdx

这条指令把地址为 0x8(%rsp) 的内存中的值拷贝到rdx寄存器,这里的0x8(%rsp) 就利用了 rsp 寄存器加偏移 8 的方式来读取内存中的值。

寄存器的内容我们就先简单的介绍到这里,但这些并不是我们需要了解的有关寄存器的全部内容,有些内容需要等我们学习了汇编指令和函数调用栈之后才能更加深刻的理解,到时候我们再回头来继续介绍相关的知识。

go语言调度器源代码情景分析之二:CPU寄存器的更多相关文章

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

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

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

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

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

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

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

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

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

    go语言runtime(包括调度器)源代码中有部分代码是用汇编语言编写的,不过这些汇编代码并非针对特定体系结构的汇编代码,而是go语言引入的一种伪汇编,它同样也需要经过汇编器转换成机器指令才能被CPU ...

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

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

  7. linux 内核源代码情景分析——Intel X86 CPU 系列的寻址方式

    当我们说一个CPU是"16位"或"32"位时,指的是处理器中"算数逻辑单元"(ALU)的宽度.数据总线通常与ALU具有相同的宽度.当Inte ...

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

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

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

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

随机推荐

  1. CVE-2017-12149 JBOOS AS 6.X 反序列化漏洞利用

    检测目录: 返回500 一般就是存在了. 下载工具: http://scan.javasec.cn/java/JavaDeserH2HC.zip 使用方法: javac -cp .:commons-c ...

  2. Flask入门之Bootstrap介绍使用和Flask-Nav快速导航栏

    一.Bootstrap Bootstrap,来自 Twitter,是目前最受欢迎的前端框架. Python中,同样可以使用Bootstrap. 1. 导入Bootstrap库 from flask_b ...

  3. HBuilder真机联调、手机运行

    第一步:先确认手机是否连接上 未连接状态 如下图所示为已连接状态 导致手机未成功连接的原因: (1)手机与电脑未用USB数据线连接(嘿嘿,这一部大家估计都做到了,可略过) (2)电脑上需要安装电脑版的 ...

  4. flask模板

    做为python web开发领域的一员,flask跟Django在很多地方用法以都是相似的,比如flask的模板 模板就是服务器端的页面,在模板中可以使用服务端的语法进行输出控制 1.模板的工作原理 ...

  5. Win10 UWP开发系列:解决Win10不同版本的Style差异导致的兼容性问题

    最近在开发一个项目时,遇到了一个奇怪的问题,项目依赖的最低版本是10586,目标版本是14393,开发完毕发布到商店后,很多用户报无法正常加载页面.经查,有问题的都是Win10 10586版本. 我上 ...

  6. http响应结构分析

    HTTP响应由三个部分组成: 1.状态码(Status Code): 描述了响应的状态.可以用来检查是否成功的完成了请求.请求失败的情况下,状态码可用来找出失败的原因.如果Servlet没有返回状态码 ...

  7. linux 下 Emacs dired 模式 隐藏 dot file ".filename" 文件

    有时候 emacs 下调用 C-x dired 是挺方便的一个事,但是一堆的点文件(linux下以"."为前缀的文件,即隐藏文件)让人目不暇接,打算隐藏之. 参考如下: 最主要的是 ...

  8. vue-quasar-admin 一个包含通用权限控制的后台管理系统

    vue-quasar-admin   Quasar-Framework 是一款基于vue.js开发的开源的前端框架, 它能帮助web开发者快速创建以下网站:响应式网站,渐进式应用,手机应用(通过Cor ...

  9. 基于.net core实现项目自动编译、并生成nuget包

    近期想对自己的项目增加自动编译并生成nuget包,网上资料不少.但总还有迷糊的时候.首先:此解决方案包含多种版本的项目,如:有编译必须是x86平台,以及还有传统的.net foramework项目,以 ...

  10. python资料分享

    python入门资料分享:链接:https://pan.baidu.com/s/1aATizMh5e0ON6xfmtxXPzA  密码:m8bf 提高资料:链接:https://pan.baidu.c ...