当前位置:正文

一口气望完45个寄存器,CPU中央技术大揭秘

admin | 2020-10-26 00:51 浏览数:

原标题:一口气望完45个寄存器,CPU中央技术大揭秘

作者 | 轩辕之风O

来源 | 编程技术宇宙(ID:xuanyuancoding)

头图 | CSDN 下载自东方IC

序言

前段时间,吾赓续写了十来篇CPU底层系列技术故事文章,有不少读者私信吾让吾写一下 CPU 的寄存器。

寄存器这个太多太复杂,不正当写故事,拖了很久,总算是写完了,这篇文章就来详细聊聊 x86/x64架构的 CPU 中那些纷繁复杂的寄存器们。

长文预警,时速较快,请系好坦然带~首飞~

自1946年冯·诺伊曼领导下诞生的世界上第一台通用电子计算机 ENIAC 至今,计算机技术已经发展了七十多载。

从当初专用于数学计算的重大无比,到后来大型机服务器时代,从幼我微机技术兴旺发展,到互联网浪潮席卷全球,再到移动互联网、云计算蒸蒸日上的当下,计算机变的形态各异,无处不在。

这七十多年中,展现了数不清的编程说话,经历这些编程说话,又开发了多数的行使程序。

可不论什么样的行使程序,什么样的编程说话,最后的程序逻辑都是要交付给 CPU 往实走实现的(自然这边有些不厉谨,除了 CPU,还有协处理器、GPU 等等)。因此晓畅和学习 CPU 的原理都是对计算机基础知识的夯实大有裨好。

在七十多年的漫长历程中,也涌现了不少架构的 CPU。

MIPS

PowerPC

x86/x64

IA64

ARM

······

这篇文章就以市场行使最为普及的x86-x64架构为现在标,经历学习晓畅它内部的100个寄存器功能作用,来串联阐述CPU底层做事原理。

经历这篇文章,你将晓畅到:

CPU指令实走原理

内存寻址技术

柔件调试技术原理

休止与变态处理

编制调用

CPU多义务技术

什么是寄存器?

寄存器是 CPU 内部用来存放数据的一些幼型存储区域,用来一时存放参与运算的数据和运算效果以及一些 CPU 运走必要的新闻。

x86 架构 CPU 走的是 复杂指令集(CISC)路线,挑供了雄厚的指令来实现重大的功能,与此同时也挑供了大量寄存器来辅助功能实现。这篇文章将遮盖下面这些寄存器:

通用寄存器

标志寄存器

指令寄存器

段寄存器

限制寄存器

调试寄存器

描述符寄存器

义务寄存器

MSR寄存器

通用寄存器

首当其冲的是通用寄存器,这些的寄存器是程序实走代码最最常用,也最最基础的寄存器,程序实走过程中,绝大片面时间都是在操作这些寄存器来实现指令功能。

所谓通用,即这些寄存器 CPU 异国稀奇的用途,交给行使程序“肆意”行使。仔细,这个肆意,吾打了引号,对于有些寄存器,CPU 有一些潜规则,用的时候要仔细。

eax: 清淡用来实走添法,函数调用的返回值清淡也放在这内里

ebx: 数据存取

ecx: 清淡用来行为计数器,比如for循环

edx: 读写I/O端口时,edx用来存放端口号

esp: 栈顶指针,指向栈的顶部

ebp: 栈底指针,指向栈的底部,清淡用ebp+偏移量的方法来定位函数存放在栈中的片面变量

esi: 字符串操作时,用于存放数据源的地址

edi: 字符串操作时,用于存放方针地址的,和esi两个往往搭配一首行使,实走字符串的复制等操作

在 x64架构中,上面的通用寄存器都扩展成为64位版本,名字也进走了升级。自然,为了兼容32位模式程序,行使上面的名字照样是能够访问的,相等于访问64位寄存器的矮32位。

raxrbxrcxrdxrsprbprsirdi

除了扩展正本存在的通用寄存器,x64架构还引入了8个新的通用寄存器:

r8-r15

在正本32位时代,函数调用时,谁人时候通用寄存器少,参数绝大多数时候是经历线程的栈来进走传递(自然也有行使寄存器传递的,比如著名的C++ this指针行使 ecx 寄存器传递,不过能用的寄存器毕竟不多)。

进入x64时代,寄存器资源裕如了,参数传递绝大多数都是用寄存器来传了。 寄存器传参的益处是速度快,缩短了对内存的读写次数。

自然,详细行使栈照样用寄存器传参数,这个不是编程说话决定的,而是编译器在编译生成 CPU 指令时决定的,倘若编译器非要在 x64 架构 CPU 上行使线程栈来传参那也不是不能,这个对高级说话是无感知的。

标志寄存器

标志寄存器,内里有多多标记位,记录了 CPU 实走指令过程中的一系列状态,这些标志大都由 CPU 自动竖立和修改:

CF 进位标志

PF 奇偶标志

ZF 零标志

SF 符号标志

OF 补码溢出标志

TF 跟踪标志

IF 休止标志

······

在 x64 架构下,正本的 eflags 寄存器升级为64位的 rflags,不过其高32位并异国新添什么功能,保留为异日行使。

指令寄存器

eip: 指令寄存器能够说是CPU中最最主要的寄存器了,它指向了下一条要实走的指令所存放的地址,CPU的做事其实就是赓续掏出它指向的指令,然后实走这条指令,同时指令寄存器赓续指向下面一条指令,如此赓续重复,这就是CPU做事的基本平时。

而在漏洞抨击中,暗客想尽手段想方设法都想要修改指令寄存器的地址,从而能够实走凶意代码。

同样的,在 x64 架构下,32位的 eip 升级为64位的 rip寄存器。

段寄存器

段寄存器与 CPU 的内存寻址技术周详有关。

早在16位的8086 CPU 时代,内存资源珍贵,CPU 行使分段式内存寻址技术:

16位的寄存器能寻址的周围是64KB,经历引入段的概念,将内存空间划分为差别的区域:分段,经历段基址+段内偏移段手段来寻址。

如许一来,段的基地址保存在那里呢?8086 CPU 特意竖立了几个段寄存器用来保存段的基地址,这就是段寄存器段的由来。

段寄存器也是16位的。

段寄存器有下面6个,前线4个是早期16位模式就引入了,到了32位时代,又新添了 fs 和 gs 两个段寄存器。

cs: 代码段

ds: 数据段

ss: 栈段

es: 扩展段

fs: 数据段

gs: 数据段

段寄存器内里存储的内容与 CPU 现在做事的内存寻址模式周详有关。

当CPU处于16位实地址模式下时,段寄存器存储段的基地址,寻址时,将段寄存器内容左移4位(乘以16)得到段基地址+段内偏移得到最后的地址。

当 CPU 做事于珍惜模式下,段寄存器存储的内容不再是段基址了,此时的段寄存器中存放的是 段选择子,用来指使现在这个段寄存器“指向”的是哪个分段。

仔细吾这边的指向打了引号,段寄存器中存储的并不是内存段的直接地址,而是段选择子,它的组织如下:

16个 bit 长度的段寄存器内容划分了三个字段:

PRL:特权乞求级,就是吾们常说的ring0-ring3四个特权级。

TI:0外示用的是全局描述符外GDT,1外示行使的是片面描述符外LDT。

Index: 这是一个外格中外项的索引值,这个外格叫 内存描述符外,它的每一个外项都描述了一个内存分段。

这边挑到了两个外,全局描述符外 GDT 和片面描述符外 LDT,关于这两个外的介绍,下面介绍描述符寄存器时再详述,这边只必要清新,这是 CPU 声援分段式内存管理必要的外格,放在内存中,外格中的每一项都是一个描述符,记录了一个内存分段的新闻。

珍惜模式下的段寄存器和段描述符到末了的内存分段,经历下图的手段有关在一首:

通用寄存器、段寄存器、标志寄存器、指令寄存器,这四组寄存器共同构成了一个基本的指令实走环境,一个线程的上下文也基本上就是这些寄存器,在实走线程切换的时候,就是修改它们的内容。

限制寄存器

限制寄存器是 CPU 中一组相等主要的寄存器,吾们清新 eflags 寄存器记录了现在运走线程的一系列关键新闻。

那 CPU 运走过程中自身的一些关键新闻保存在那里呢?答案是限制寄存器!

32位 CPU 统统有cr0-cr4共5个限制寄存器,64位增补了 cr8。他们各自有差别的功能,但都存储了 CPU 做事时的主要新闻:

cr0: 存储了CPU限制标记和做事状态

cr1: 保留未行使

cr2: 页舛讹显眼前保存导致出错的地址

cr3: 存储了现在进程的虚拟地址空间的主要新闻—— 页现在录地址

cr4: 也存储了CPU做事有关以及现在人义务的一些新闻

cr8: 64位新添扩展行使

其中,CR0 尤其主要,它包含了太多主要的 CPU 新闻,值得单独关注一下:

一些主要的标记位含义如下:

PG: 是否启用内存分页

AM: 是否启用内存对齐自动检查

WP: 是否开启内存写珍惜,若开启,对只读页面尝试写时兴将触发变态,这一机制往往被用来实现 写时复制功能

PE: 是否开启珍惜模式

除了 CR0,另一个值得关注的寄存器是 CR3,它保存了现在进程所行使的虚拟地址空间的页现在录地址,能够说是整个虚拟地址翻译中的顶级指挥棒,在进程空间切换的时候,CR3 也将同步切换。

调试寄存器

在x86/x64 CPU 内部,还有一组用于声援柔件调试的寄存器。

调试,对于吾们程序员是习以为常,必备技能。但你想过你的程序能够被调试背后的原理吗?

程序能够被调试,关键在于能够被休止实走和恢复实走,被休止的地方就是吾们竖立的断点。那程序是如何能在遇到断点的时候停下来呢?

对于一些注释实走(PHP、Python、Java)或虚拟机实走(Java)的高级说话,这很容易办到,由于它们的实走都在注释器/虚拟机的掌控之中。

而对于像C、C++如许的“底层”编程说话,程序代码是直接编译成CPU的机器指令来实走的,这就必要 CPU 来挑供对于调试的声援了。

对于清淡的断点,也就是程序实走到某个位置下就停下来,这栽断点实现的手段,在x86/x64上,是行使了一条 柔休止指令: int 3来进走实现的。

仔细,这边的 int不是指高级说话内里的整数,而是外示 interrupt 休止的有趣,是一条汇编指令,int 3则外示休止向量号为3的休止。

在吾们行使调试器下断点时,调试器将会把对答位置的正本的指令替换为一个 int 3 指令,机器码为 0xCC。这个动刁难吾们是透明的,吾们在调试器中望到的照样是正本的指令,但实际上内存中已经不是正本的指令了。

趁便挑一句,两个 0xCC 是汉字【烫】的编码,在一些编译器里,会给线程的栈中填充大量的 0xCC,倘若程序出错的时候,吾们往往会望到许多 烫烫烫展现,就是这个因为。

言归正传,CPU在实走这条int 3指令时,将自动触发休止处理流程(固然这实际上不是一个真实的休止),CPU将掏出IDTR寄存器指向的休止描述符外IDT的第3项,实走内里的休止处理函数。

而这个休止描述符外,早在操作编制启动之初,就已经挑前安排好了,因此实走这条指令后,操作编制的休止处理函数将介入,来处理这一事件。

后面的过程就多了,浅易来说,操作编制会把触发这一事件的进程凝结首来,随后将这一事件发送到调试器,调试器拿到之后就清新现在标进程触发断点了。这个时候,咱们程序员就能经历调试器的UI交互奇米大香蕉伊人或者命令走调试接口来调试现在标进程,查望堆栈、查望内存、变量都随你。

倘若吾们要赓续运走,调试器将会把之前修改的int 3指令给恢复回往,然后告知操作编制:吾处理完了,把现在标进程解冻吧!

上面浅易描述了一下清淡断点的实现原理。现在思考一个场景:吾们发现一个bug,某个全局整数型变量的值老是莫名其妙被修改,但你发现有许多线程,许多函数都有能够会往修改这个变量,你想找出到底谁干的,怎么办?

这个时候上面的清淡断点就没手段了,你必要一栽新的断点: 硬件断点。

这时候就该本末节的主人公调试寄存器登场外演了。

在x86架构CPU内部,挑供了8个调试寄存器DR0~DR7。

DR0~DR3:这是四个用于存储地址的寄存器

DR4~DR5:这两个有点稀奇,受前线挑到的CR4寄存器中的标志位DE位限制,倘若CR4的DE位是1,则DR4、DR5是不能访问的,访问将触发变态。倘若CR4的DE位是0,则DR4和DR5将会变成DR6和DR7的又名,相等于做了一个柔链接。如许做是为了将DR4、DR5保留,以便异日扩展调试功能时行使。

DR6:这个寄存器中存储了硬件断点触发后的一些状态新闻

DR7:调试限制寄存器,这内里记录了对DR0-DR3这四个寄存器中存储地址的休止手段(是对地址的读,照样写,照样实走)、数据长度(1/2/4个字节)以及作用周围等新闻

经历调试器的接口竖立硬件断点后,CPU在实走代码的过程中,倘若已足条件,将自动休止下来。

回答前线挑出的题目,想要找出是谁偷偷修改了全局整形变量,只必要经历调试器竖立一个硬件写入断点即可。

描述符寄存器

所谓 描述符,其实就是一个数据组织,用来记录一些新闻,‘描述’一个东西。把许多个描述符排列在一首,构成一个外,就成了描述符外。再行使一个寄存器来指向这个外,这个寄存器就是 描述符寄存器。

在x86/x64系列CPU中,有三个特意主要的描述符寄存器,它们别离存储了三个地址,指向了三个特意主要的描述符外。

gdtr: 全局描述符外寄存器,前线挑到,CPU现在行使的是段+分页结相符的内存管理手段,那编制统统有那些分段呢?这就存储在一个叫全局描述符外( GDT)的外格中,并用gdtr寄存器指向这个外。这个外中的每一项都描述了一个内存段的新闻。

ldtr:片面描述符外寄存器,这个寄存器和上面的gdtr相通,同样指向的是一个段描述符外( LDT)。差别的是,GDT是全局唯一,LDT是片面行使的,能够创建多个,随着义务段切换而切换(下文介绍义务寄存器会挑到)。

GDT和LDT中的外项,就是段描述符,描述了一个内存分段的新闻,其组织如下:

一个外项占有8个字节(32位CPU),内里存储了一个内存分段的诸多新闻:基地址、大幼、权限、类型等新闻。

除了这两个段描述符寄存器,还有一个特意主要的描述符寄存器:

idtr:休止描述符外寄存器,指向了 休止描述符外IDT,这个外的每一项都是一个休止处理描述符,当CPU实走过程中发生了硬休止、变态、柔休止时,将自动从这个外中定位对答的外项,内里记录了发生休止、变态时该往那里实走处理函数。

IDT中的外项称为 Gate,中文有趣为 门,由于这是行使程序进入内核的主要入口。固然外的名字叫休止描述符外,但外中存储的不全是休止描述符,IDT中的外项存在三栽类型,对答三栽类型的门:

义务门

组织门

休止门

三栽描述符中都存储了处理这个休止/变态/义务时该往那里处理的地址。三栽门用途纷歧,其中休止门是真实意义上的休止,而像前线挑到的调试指令int 3以及老式的编制调用指令int 2e/int 80都属于组织门。义务门则用的较少,要晓畅义务门,先晓畅下义务寄存器。

义务寄存器

当代操作编制,都是声援多义务并发运走的,x86架构CPU为了顺答时代潮流,在硬件层面上挑供了特意的机制用来声援多义务的切换,这表现在两个方面:

CPU 内部竖立了一个专用的寄存器—— 义务寄存器 TR,它指向现在运走的义务。

定义了描述义务的数据组织 TSS,内里存储了一个义务的上下文(一系列寄存器的值),下图是一个32位 CPU 的 TSS 组织图:

x86CPU 的构想是每一个义务对答一个 TSS,然后由 TR 寄存器指向现在的义务,实走义务切换时,修改 TR 寄存器的指向即可,这是硬件层面的多义务切换机制。

这个构想其实照样很不错的,然而实际却打了脸,包括 Linux 和 Windows 在内的主流操作编制都异国行使这个机制来进走线程切换,而是本身行使柔件来实现多线程切换。

因此,绝大多数情况下,TR 寄存器都是指向固定的,即便线程切换了,TR 寄存器照样不会转折。

仔细,吾这边说的的是绝大多数情况,而异国说物化。固然操作编制不仰仗 TSS 来实现多义务切换,但这并意外味着 CPU 挑供的 TSS 操作系同一点也异国行使。照样存在一些稀奇情况,如一些变态处理会行使到 TSS 来实走处理。

下面这张图,展现了限制寄存器、描述符寄存器、义务寄存器构成的全貌:

模型特定寄存器

从80486之后的x86架构 CPU,内部增补了一组新的寄存器,统称为 MSR 寄存器,中文直译是模型特定寄存器,有趣是这些寄存器不像上面列出的寄存器是固定的,这些寄存器能够随着差别的版本有所转折。这些寄存器主要用来声援一些新的功能。

随着x86 CPU 赓续更新换代,MSR 寄存器变的越来越多,但与此同时,有一片面 MSR 寄存器随着版本迭代,徐徐固化下来,成为了转折中那片面不变的,这片面 MSR 寄存器,Intel 将其称为 Architected MSR,这片面 MSR 寄存器,在命名上,同一添上了 IA32的前缀。

这边选取三个代外性的 MSR 浅易介绍一下:

IA32_SYSENTER_CS

IA32_SYSENTER_ESP

IA32_SYSENTER_EIP

这三个MSR寄存器是用来实现 迅速编制调用。

在早期的x86架构 CPU 上,编制调用倚赖于柔休止实现,相通于前线调试用到的int 3指令,在 Windows 上,编制调用用到的是 int 2e,在 Linux 上,用的是 int 80。

柔休止毕竟照样比较慢的,由于实走柔休止就必要内存查外,经历 IDTR 定位到 IDT,再掏出函数进走实走。

编制调用是一个屡次触发的行为,如此这般势必对性能有所影响。在进入奔腾时代后,就添上了上面的三个 MSR 寄存器,别离存储了实走编制调用后,内核编制调用入口函数所必要的段寄存器、堆栈栈顶、函数地址,不再必要内存查外。迅速编制调用还挑供了特意的 CPU 指令 sysenter/sysexit 用来发首编制调用和退出编制调用。

在64位上,这一对指令升级为 syscall/sysret。

总结

以上就是通盘要介绍的寄存器了,必要表明一下的是,这并不是 x86 CPU 通盘一切的寄存器,除了这些,还存在 XMM、MMX、FPU 浮点数运算等其他寄存器。

这篇文章以 x86/x64 架构 CPU 为现在标,经历对 CPU 内部寄存器的阐述,串讲了 CPU 实走代码机制、内存寻址技术、休止与变态处理、多义务管理、编制调用、调试原理等多栽计算机底层知识。

Powered by 中文字幕无线观看在 @2018 RSS地图 html地图

Copyright 365建站 © 2012-2013 365建站器 版权所有