写在前面

  此系列是本人一个字一个字码出来的,包括示例和实验截图。如有好的建议,欢迎反馈。码字不易,如果本篇文章有帮助你的,如有闲钱,可以打赏支持我的创作。如想转载,请把我的转载信息附在文章后面,并声明我的个人信息和本人博客地址即可,但必须事先通知我

你如果是从中间插过来看的,请仔细阅读 羽夏看Linux系统内核——简述 ,方便学习本教程。

中断

  中断通常是由CPU外部的输入输出设备(硬件)所触发的,供外部设备通知CPU有事情需要处理,因此又叫中断请求,英文为Interrupt Request。中断请求的目的是希望CPU暂时停止执行当前正在执行的程序,转去执行中断请求所对应的中断处理例程,中断处理程序由哪有IDT表决定。

  80x86有两条中断请求线:非屏蔽中断线,NMI,全称NonMaskable Interrupt和可屏蔽中断线,INTR,全称Interrupt Require

不可屏蔽中断

  什么是不可屏蔽中断CPUEFLAG之中有一个位,它是IF位。如果它被置0。如果有可屏蔽中断告诉CPU有中断来了,你能先执行我的代码呢?可是IF位是0,对不起,我听不见。左耳朵进,右耳朵出。反之,我会处理。常见的不可屏蔽中断有电脑长按关机、键盘输入等等。当非可屏蔽中断产生时,CPU在执行完当前指令后会里面进入中断处理程序,非可屏蔽中断不受那个位的影响,一旦发生,CPU必须处理。为了方便观看,给个EFLAG图解:

  那么CPU是如何处理我们的不可屏蔽中断呢?我们先来看如下表格:

(IDT表)中断号 NMI 说明
0x2 不可屏蔽中断 80x86 中固定为 0x2

  如果处理不可屏蔽中断,CPU会调用2号中断。涉及的IDT表和中断门的知识如果忘却请查看前面的教程。

可屏蔽中断

  什么是可屏蔽中断,我就不赘述了。在硬件级,可屏蔽中断是由一块专门的芯片来管理的,通常称为中断控制器。它负责分配中断资源和管理各个中断源发出的中断请求.为了便于标识各个中断请求,中断管理器通常用IRQ,全称为Interrupt Request,后面加上数字来表示不同的中断。

  那么CPU是如何处理我们的可屏蔽中断呢?我们先来看如下表格:

(IDT表)中断号 IRQ 说明
0x30 IRQ0 时钟中断
0x31-0x3F IRQ1-IRQ15 其他硬件设备的中断

  如果自己的程序执行时不希望CPU去处理这些中断,可以用CLI指令清空EFLAG寄存器中的IF位,用STI指令设置EFLAG寄存器中的IF位。

  硬件中断与IDT表中的对应关系并非固定不变的,可以参考白皮书的Chapter 10 Advanced Programmable Interrupt Controller(APIC)进行了解。

异常

  异常通常是CPU在执行指令时检测到的某些错误,比如除0、访问无效页等。中断与异常之间有一些相似之处,但它们是不一样的:中断来自于外部设备,是中断源(比如键盘)发起的,CPU是被动的;而异常来自于CPU本身,是CPU主动产生的。INT N虽然被称为“软件中断”,但其本质是异常,EFLAGIF位对INT N是无效。

异常处理

  无论是由硬件设备触发的中断请求还是由CPU产生的异常,处理程序都在IDT表。常见的异常处理程序如下表所示:

错误类型 (IDT表)中断号
页错误 0xE
段错误 0xD
除零错误 0x0
双重错误 0x8

控制寄存器

  控制寄存器用于控制和确定CPU的操作模式。控制寄存器有Cr0Cr1Cr2Cr3Cr4Cr1被保留了,Cr3用于页目录表基址,其他的将继续详细讲解。

Cr0

  Cr0是一个十分重要的寄存器,可以说它是总开关的集合体。如下图所示:

  PE位是启用保护模式(Protection Enable)标志。若PE = 1是开启保护模式,反之为实地址模式。这个标志仅开启段级保护,而并没有启用分页机制。若要启用分页机制,那么PEPG标志都要置位。

  PG位是启用分页机制。在开启这个标志之前必须已经或者同时开启PE标志。PG = 0PE = 0,处理器工作在实地址模式下。PG = 0PE = 1,处理器工作在没有开启分页机制的保护模式下。PG = 1PE = 0,在PE没有开启的情况下无法开启PGPG = 1PE = 1,处理器工作在开启了分页机制的保护模式下。

  WP位对于Intel 80486或以上的CPU,是写保护(Write Proctect)标志。当设置该标志时,处理器会禁止超级用户程序(例如特权级0的程序)向用户级只读页面执行写操作;当CPL < 3的时候,如果WP = 0可以读写任意用户级物理页,只要线性地址有效。如果WP = 1可以读取任意用户级物理页,但对于只读的物理页,则不能写。

Cr2

  当CPU访问某个无效页面时,会产生缺页异常,此时,CPU会将引起异常的线性地址存放在CR2中,如下图所示:

Cr4

  Cr4的结构如下图所示:

  VME用于虚拟8086模式。PAE用于确认是哪个分页,PAE = 1,是2-9-9-12分页,PAE = 010-10-12分页。PSE是大页是否开启的总开关,如果置0,就算PDE中设置了大页你也得是普通的页。至于分页到底是什么,将会在下一篇进行讲解。

中断小节

  有些结构的位我并没有详细介绍,详情请查看白皮书的控制寄存器的篇章,如下图所示:

  如果对分页不了解看不懂没关系,下一篇将会介绍相关知识。

分页

  在讲解分页基础之前,我们先大体了解CPU是如何在保护模式下访问数据的,如下图所示:

  比如我们执行mov eax,ds:[0x12345678]这句汇编指令的时候,0x12345678这个线性地址会传递给CPU,先查询TLB缓存有没有,有的话直接取出来返回;如果没有,经过MMU(内存管理单元)处理得到物理地址,通过固定的分页模式直接找到,取出数据返回。

  前面的教程讲解了段的机制,接下来将介绍页的机制。CPU为了方便管理物理内存,按照页的方式进行管理内存。可用的所有内存可以类比为一本书,而所有的内存被分为这本书的一个页。对于32位来说,它有10-10-12分页和2-9-9-12分页。其中10-10-12分页最为简单,故拿其作为详细讲解,作为分页讲解的基础。

  我们都了解一个进程都有4GB的虚拟地址空间,它们并不是真正的地址,而是个索引。它通过某种方式进行转换,从而指向真正的物理地址,示意图如下所示:

  而虚拟地址也被称作线性地址。举个例子,比如某个进程里面我想读取一个0x12345678,它就是线性地址,通过一些转换,找到了对应的物理地址0x10101010,如下图所示:

  每个进程都有一个CR3,准确的说是都一个CR3的值。CR3本身是个寄存器,一核一套。CR3里面放的是一个真正的物理地址,指向一个物理页,一共4096字节,如下图所示:

  对于10-10-12分页来说,线性地址对应的物理地址是有对应关系的,它被分成了三个部分,每个部分都有它具体的含义。线性地址分配的结构如下图所示:

  第一个部分指的是PDEPDT的索引,第二部分是PTEPTT的索引,第三个部分是在PTE指向的物理页的偏移。PDT被称为页目录表,PTT被称为页表。PDEPTE分别是它们的成员,大小为4个字节。我们接下来将详细介绍每一个部分是咋用的。

10-10-12 分页整体结构

  通过实验我们了解了它们的结构,接下来将详细介绍了。根据实验结果的体验,可以给出如下图:

  分页并不是由操作系统决定的,而是由CPU决定的。只是操作系统遵守了CPU的约定来实现的。物理页是什么?物理页是操作系统对可用的物理内存的抽象,按照4KB的大小进行管理(Intel是按照这个值做的,别的CPU就不清楚了),和真实硬件层面上的内存有一层的映射关系,这个不是保护模式的范畴,故不介绍。

PDE 与 PTE

  前面我们简单了解PDEPTE,接下来将学习它们的属性结构,结构如下:

P 位

  表示PDE或者PTE是否有效,如果有效为1,反之为0

R/W 位

  如果R/W = 0,表示是只读的,反之为可读可写。

U/S 位

  如果U/S = 0,则为特权用户(super user),即非3环权限。反之,则为普通用户,即为3环权限。

PS位

  这个位只对PDE有意义。如果PS == 1,则PDE直接指向物理页,不再指向PTE,低22位是页内偏移。它的大小为4MB,俗称“大页”。

A 位

  是否被访问,即是否被读或者写过,如果被访问过则置1

D 位

  脏位,指示是否被写过。若没有被写过为0,被写过为1

注意,下面的三个位的讲解将涉及 TLB 和控制寄存器相关知识,为了保证文章的完整性,故先介绍。之后将会详细讲解。

G 位

  表示是否为全局页。它的作用是什么呢?举个例子,操作系统的进程的高2G映射基本不变,如果Cr3改了,TLB刷新重建高2G以上很浪费。所以PDEPTE中有个G位,如果为1,刷新TLB时将不会刷新它指向的页。

PWT 位

  当PWT = 1,写缓存的时候也要将数据写入内存中。

PCD 位

  当PCD = 1时,禁止某个页写入缓存,直接写内存。比如,做页表用的页,已经存储在TLB中了,可能不需要再缓存了。

注意事项

  • PTE可以没有物理页,且只能对应一个物理页。
  • 多个PTE也可以指向同一个物理页。
  • PDEPTE重合的属性共同决定着最终物理页的属性。比如P位,如果有一个是0,那么最终的物理页就是无效的。但是PDEPTE它们的属性的影响范围是不一样的。数值上:物理页的属性 = PDE属性 & PTE属性。

PAE 分页

  PAE分页是啥,其实他就是2-9-9-12分页的英文缩写。为什么要有2-9-9-12分页,其实还是物理页不够用了,需要扩展。但想要足够的物理页,位数在那里,你想大也大不了。那么我就需要扩展物理页地址的位数,于是乎2-9-9-12分页诞生了,它整体分页的结构如下:

  与10-10-12分页不同的地方就是,多了一层名为页目录指针表的东西,英文缩写为PDPTT。每个PDEPTE被扩展为8个字节,物理地址描述的位数扩展为24位,故可以描述更多的物理页,但个数减半,变成了512个。下面详细查看它们的结构。

  首先看PDPTT的结构。由2-9-9-122可知第一部分由两位二进制组成,那么最多有4种结果。也就是为什么有五个成员,它的结构如下图所示:

  然后是PDE,既然学过了10-10-12分页,直接看下面的结构示意图吧:

非大页

大页

  然后是PTE,同理不多说了:

  我们之前做一道作业题目知道,我写的shellcode写到一个页上,它并没有执行权限,但它不是代码,仍然可以被我执行。为了弥补这个漏洞,Intel给我们补了一个硬件层面上的漏洞,它是一个位,处于PDEPTE的最高位,如下图所示:

  如果最高位是1,说明被保护。如果这个是数据,且这个X位被置为1,则会被报出异常不能执行。反之,和正常的10-10-12分页没什么两样。

  一个进程的线性地址仍是4GB的线性空间,有再多的物理页有啥用呢?在10-10-12分页下,假设进程一启动,就把所有的物理页都挂上,且没有任何交换。那么只能启动一个;如果在2-9-9-12分页下,同样的情况,它可以启动4个进程。这个就是2-9-9-12分页的意义。

TLB

  CPU通过页的方式对物理内存进行了,需要通过某种运算方式才能访问真正的内存。但是,如果频繁访问某一个线性地址,每次都得通过推算到真正的物理地址然后进行读写操作,是不是挺浪费效率的?Intel就考虑到性能的问题,提供了TLB这一个机制,提供缓存提高读写效率。

  TLB的全称为Translation Lookaside Buffer,它的结构如下:

LA(线性地址) PA(物理地址) ATTR(属性) LRU(统计)

  对于TLB,给出如下说明:

  1. ATTR(属性):如果是2-9-9-12分页,属性是PDPEPDEPTE三个属性共同决定的。如果是10-10-12分页就是PDEPTE共同决定。

  2. 不同的CPU这个表的大小不一样。

  3. 只要Cr3变了,TLB立马刷新,一核一套TLB

  如果Cr3改了,TLB刷新重建高2G以上很浪费。所以PDEPTE中有个G标志位,如果G位为1刷新TLB时将不会刷新PDE/PTEG位为1的页,当TLB满了,根据统计信息将不常用的地址废弃,最近最常用的保留。

  TLB有不同的种类,用于不同的缓存目的,它在X86体系里的实际应用最早是从Intel486CPU开始的,在X86体系的CPU里边,一般都设有如下4组TLB

  第一组:缓存一般页表(4K字节页面)的指令页表缓存:Instruction-TLB

  第二组:缓存一般页表(4K字节页面)的数据页表缓存:Data-TLB

  第三组:缓存大尺寸页表(2M/4M字节页面)的指令页表缓存:Instruction-TLB

  第四组:缓存大尺寸页表(2M/4M字节页面)的数据页表缓存:Data-TLB

CPU缓存

  CPU缓存是位于CPU与物理内存之间的临时存储器,它的容量比内存小的多但是交换速度却比内存要快得多。它可以做的很大,但不是TLB,它们有很大的不同。TLB存的是线性地址与物理地址的对应关系,CPU缓存存的是物理地址与内容对应关系。

  更多的细节请参考白皮书的Chapter 11 Memory Cache Control,本篇教程主要是针对内核安全层面,就不再赘述了。

PWT 与 PCD

  PWT全称为Page Write ThroughPWT = 1时,写Cache的时候也要将数据写入内存中。

  PCD全称为Page Cache DisablePCD = 1时,禁止某个页写入缓存,直接写内存。比如,做页表用的页,已经存储在TLB中了,可能不需要再缓存了。

下一篇

  羽夏看Linux内核——内核加载

羽夏看Linux内核——中断与分页相关入门知识的更多相关文章

  1. 羽夏看Linux内核——门相关入门知识

    写在前面   此系列是本人一个字一个字码出来的,包括示例和实验截图.如有好的建议,欢迎反馈.码字不易,如果本篇文章有帮助你的,如有闲钱,可以打赏支持我的创作.如想转载,请把我的转载信息附在文章后面,并 ...

  2. 羽夏看Linux内核——启动那些事

    写在前面   此系列是本人一个字一个字码出来的,包括示例和实验截图.如有好的建议,欢迎反馈.码字不易,如果本篇文章有帮助你的,如有闲钱,可以打赏支持我的创作.如想转载,请把我的转载信息附在文章后面,并 ...

  3. 羽夏看Linux内核——引导启动(下)

    写在前面   此系列是本人一个字一个字码出来的,包括示例和实验截图.如有好的建议,欢迎反馈.码字不易,如果本篇文章有帮助你的,如有闲钱,可以打赏支持我的创作.如想转载,请把我的转载信息附在文章后面,并 ...

  4. 羽夏看Linux内核——段相关入门知识

    写在前面   此系列是本人一个字一个字码出来的,包括示例和实验截图.如有好的建议,欢迎反馈.码字不易,如果本篇文章有帮助你的,如有闲钱,可以打赏支持我的创作.如想转载,请把我的转载信息附在文章后面,并 ...

  5. 羽夏看Linux内核——引导启动(上)

    写在前面   此系列是本人一个字一个字码出来的,包括示例和实验截图.如有好的建议,欢迎反馈.码字不易,如果本篇文章有帮助你的,如有闲钱,可以打赏支持我的创作.如想转载,请把我的转载信息附在文章后面,并 ...

  6. 羽夏看Linux内核——环境搭建

    写在前面   此系列是本人一个字一个字码出来的,包括示例和实验截图.如有好的建议,欢迎反馈.码字不易,如果本篇文章有帮助你的,如有闲钱,可以打赏支持我的创作.如想转载,请把我的转载信息附在文章后面,并 ...

  7. 羽夏看Win系统内核——保护模式篇

    写在前面   此系列是本人一个字一个字码出来的,包括示例和实验截图.由于系统内核的复杂性,故可能有错误或者不全面的地方,如有错误,欢迎批评指正,本教程将会长期更新. 如有好的建议,欢迎反馈.码字不易, ...

  8. 羽夏看Win系统内核——驱动篇

    写在前面   此系列是本人一个字一个字码出来的,包括示例和实验截图.由于系统内核的复杂性,故可能有错误或者不全面的地方,如有错误,欢迎批评指正,本教程将会长期更新. 如有好的建议,欢迎反馈.码字不易, ...

  9. 羽夏看Win系统内核——进程线程篇

    写在前面   此系列是本人一个字一个字码出来的,包括示例和实验截图.由于系统内核的复杂性,故可能有错误或者不全面的地方,如有错误,欢迎批评指正,本教程将会长期更新. 如有好的建议,欢迎反馈.码字不易, ...

随机推荐

  1. 差分隐私(Differential Privacy)定义及其理解

    1 前置知识 本部分只对相关概念做服务于差分隐私介绍的简单介绍,并非细致全面的介绍. 1.1 随机化算法 随机化算法指,对于特定输入,该算法的输出不是固定值,而是服从某一分布. 单纯形(simplex ...

  2. ESP8266远程控制电子门

    ESP8266远程控制电子门 最前面介绍: 这是一个使用ESP8266 联网控制继电器,实现手机远程控制电子门,打开关闭,开关一次的物联网联手小项目 附git地址:https://github.com ...

  3. Crane-scheduler:基于真实负载进行调度

    作者 邱天,腾讯云高级工程师,负责腾讯云 TKE 动态调度器与重调度器产品. 背景 原生 kubernetes 调度器只能基于资源的 resource request 进行调度,然而 Pod 的真实资 ...

  4. 题解0014:信奥一本通1472——The XOR Largest Pair(字典树)

    题目链接:http://ybt.ssoier.cn:8088/problem_show.php?pid=1472 题目描述:在给定的 N 个整数中选出两个进行异或运算,求得到的结果最大是多少. 看到这 ...

  5. .NET中的迭代器(Iterator)

    更新记录 本文迁移自Panda666原博客,原发布时间:2021年6月30日. 一.迭代器介绍 C#2.0开始,我们可以使用迭代器(iterator).编译器自动把我们定义的迭代器生成 可枚举类型 或 ...

  6. SpringCloudAlibaba学习(解决SpringBoot初始化以及Nginx启动出错问题)

    微服务强调每个服务都是单独的数据库 在不使用微服务的情况下可以采用分布式架构,通过Template来调用远程的Rest接口 但这种方式维护起来很麻烦,而且有很多弊端. 一.环境搭建 1.首先搭建Spr ...

  7. JavaScript易错知识点

    JavaScript易错知识点整理1.变量作用域上方的函数作用域中声明并赋值了a,且在console之上,所以遵循就近原则输出a等于2. 上方的函数作用域中虽然声明并赋值了a,但位于console之下 ...

  8. Pisa-Proxy 之 SQL 解析实践

    SQL 语句解析是一个重要且复杂的技术,数据库流量相关的 SQL 审计.读写分离.分片等功能都依赖于 SQL 解析,而 Pisa-Proxy 作为 Database Mesh 理念的一个实践,对数据库 ...

  9. Linux安装fastdfs集群部署

    过程问题: make: gcc:命令未找到 解决: yum -y install gcc 一.环境和版本: Linux环境:CentOS 7.6 libfastcommon版本:1.0.39 Fast ...

  10. Java 向数组中添加元素

    一般数组是不能添加元素的,因为他们在初始化时就已定好长度了,不能改变长度. 向数组中添加元素思路 第一步:把数组转化为集合 list = Arrays.asList(array); 第二步:向集合中添 ...