TLB flush操作

一、前言

Linux VM subsystem在很多场合都需要对TLB进行flush操作,本文希望能够把这个知识点相关的方方面面描述清楚。第二章描述了一些TLB的基本概念,第三章描述了ARM64中TLB的具体硬件实现,第四章描述了linux中和TLB flush相关的软件接口。内核版本依然是4.4.6版本。

二、基本概念

1、什么是TLB?

TLB的全称是Translation Lookaside Buffer,我们知道,处理器在取指或者执行访问memory指令的时候都需要进行地址翻译,即把虚拟地址翻译成物理地址。而地址翻译是一个漫长的过程,需要遍历几个level的Translation table,从而产生严重的开销。为了提高性能,我们会在MMU中增加一个TLB的单元,把地址翻译关系保存在这个高速缓存中,从而省略了对内存中页表的访问。

2、为什么有TLB?

TLB这个术语有些迷惑,但是其本质上就是一种cache,既然是一种cache,那么就没有什么好说的,当然其存在就是为了更高的performance了。不同于instruction cache和data cache,它是Translation cache。对于instruction cache,它是解决cpu获取main memory中的指令数据(地址保存在PC寄存器中)的速度比较慢的问题而设立的。同样的data cache是为了解决数据访问指令比较慢而设立的。但是实际上这只是事情的一部分,我们来仔细看看程序中的数据访问指令(例如说是把)的执行过程,这个过程可以分成如下几个步骤:

(1)将PC中的虚拟地址翻译成物理地址

(2)从memory中获取数据访问指令(假设该指令需要访问地址x)

(3)将虚拟地址x翻译成物理地址y

(4)从location y的memory中获取具体的数据

instruction cache解决了step (2)的性能问题,data cache解决了step (4)中的性能问题,当然,复杂的系统会设立了各个level的cache用来缓存main memory中的数据,因此,实际上unified cache同时可以加快step (2)和(4)的速度。Anyway,这只是解决了部分的问题,IC设计工程师怎么会忽略step (1)和step (3)呢,这也就是TLB的由来。如果CPU core发起的地址翻译过程能够在TLB(translation cache)中命中(cache hit),那么CPU不需要访问慢速的main memory从而加快了CPU的performance。

3、TLB工作原理

大概的原理图如下(图片来自Computer Organization and Design 5th):

当需要转换VA到PA的时候,首先在TLB中找是否有匹配的条目,如果有,那么我们称之TLB hit,这时候不需要再去访问页表来完成地址翻译。不过TLB始终是全部页表的一个子集,因此也有可能在TLB中找不到。如果没有在TLB中找到对应的item,那么称之TLB miss,那么就需要去访问memory中的page table来完成地址翻译,同时将翻译结果放入TLB,如果TLB已经满了,那么还要设计替换算法来决定让哪一个TLB entry失效,从而加载新的页表项。简单的描述就是这样了,我们可以对TLB entry中的内容进行详细描述,它主要包括:

(1) 物理地址(更准确的说是physical page number)。这是地址翻译的结果。

(2) 虚拟地址(更准确的说是virtual page number)。用cache的术语来描述的话应该叫做Tag,进行匹配的时候就是对比Tag。

(3) Memory attribute(例如:memory type,cache policies,access permissions)

(4) status bits(例如:Valid、dirty和reference bits)

(5) 其他相关信息。例如ASID、VMID,下面的章节会进一步描述。

三、ARMv8的TLB

我们选择Cortex-A72 processor来描述ARMv8的TLB的组成结构以及维护TLB的指令。

1、TLB的组成结构。下图是A72的功能block:

A72实现了2个level的TLB,绿色是L1 TLB,包括L1 instruction TLB(48-entry fully-associative)和L1 data TLB(32-entry fully-associative)。黄色block是L2 unified TLB,它要大一些,可以容纳1024个entry,是4-way set-associative的。当L1 TLB发生TLB miss的时候,L2 TLB是它们坚强的后盾。

通过上图,我们还可以看出:对于多核CPU,每个processor core都有自己的TLB。

2、如何确定TLB match

整个地址翻译过程并非简单的VA到PA的映射那么简单,其实系统中的虚拟地址空间有很多,而每个地址空间的翻译都是独立的:

(1)操作系统中的每一个进程都有自己独立的虚拟地址空间。在各个进程不同的虚拟地址空间中,相同的VA被翻译成不同的PA。

(2)如果支持虚拟化,系统中存在一个host OS和多个guest OS,不同OS之间,地址翻译是不同的,而对于一个guest OS内部,其地址空间的情况请参考(1)。

(3)如果支持TrustZone,secure monitor、secure world以及normal world是不同的虚拟地址空间。

当然,我们可以在TLB匹配过程中,不考虑上面的复杂情况,比如在进程切换的时候,在切换虚拟机的时候,或者在切换secure/normal world的时候,将TLB中的所有内容全部flush掉(全部置为无效),这样的设计当然很清爽,但是性能会大打折扣。因此,实际上在设计TLB的时候,往往让TLB entry包括了和虚拟地址空间context相关的信息。在A72中,只有满足了下面的条件,才能说匹配了一个TLB entry:

(1)请求进行地址翻译的VA page number等于TLB entry中的VA page number

(2)请求进行地址翻译的memory space identifier等于TLB entry中的memory space identifier。所谓memory space identifier其实就是区分请求是来自EL3 Exception level、Nonsecure EL2 Exception level或者是Secure and Non-secure EL0还是EL1 Exception levels。

(3)如果该entry被标记为non-Global,那么请求进行地址翻译的ASID(保存在TTBRx中)等于TLB entry中的ASID。

(4)请求进行地址翻译的VMID(保存在VTTBR寄存器中)等于TLB entry中的VMID

3、进程切换和ASID(Address Space Identifier)

如果了解OS的基本知识,那么我们都知道:每个进程都有自己独立的虚拟地址空间。如果TLB不标识虚拟地址空间,那么在进程切换的时候,虚拟地址空间也发生了变化,因此TLB中的所有的条目都应该是无效了,可以考虑invalidate all。但是,这么做从功能上看当然没有问题,但是性能收到了很大的影响。

一个比较好的方案是区分Global pages (内核地址空间)和Process-specific pages(参考页表描述符的nG的定义)。对于Global pages,地址翻译对所有操作系统中的进程都是一样的,因此,进程切换的时候,下一个进程仍然需要这些TLB entry,因而不需要flush掉。对于那些Process-specific pages对应的TLB entry,一旦发生切换,而TLB又不能识别的话,那么必须要flush掉上一个进程虚拟地址空间的TLB entry。如果支持了ASID,那么情况就不一样了:对于那些nG的地址映射,它会有一个ASID,对于TLB的entry而言,即便是保存多个相同虚拟地址到不同物理地址的映射也是OK的,只要他们有不同的ASID。

切换虚拟机和VMID的概念是类似的,这里就不多说了。

4、TLB的一致性问题

TLB也是一种cache,有cache也就意味着数据有多个copy,因此存在一致性(coherence)问题。和数据、指令cache或者unified cache不同的是,硬件并不维护TLB的coherence,一旦软件修改了page table,那么软件也需要进行TLB invalidate操作,从而维护了TLB一致性。

5、TLB操作过程

我们以一个普通内存访问指令为例,说明TLB的操作过程,在执行该内存访问指令的过程中,第一件需要完成的任务就是将要访问的虚拟地址翻译成物理地址,具体操作步骤如下:

(1)首先在L1 data TLB中寻找匹配的TLB entry(如果是取指操作,那么会在L1 instruction TLB中寻找),如果运气足够好,TLB hit,那么一切都结束了,否者进入下一步

(2)在L2 TLB中寻找匹配的TLB entry。如果不能命中,那么就需要启动hardware translation table walk了

(3)在执行hardware translation table walk的时候,是直接访问main memory还是通过L2 cache 访问呢?其实都可以的,这和系统配置有关(具体参考TCR_ELx)。如果配置的是Normal memory, Inner Write-Back Cacheable,那么可以在L2 cache中来寻找page table。如果配置的是Normal memory, Inner Write-Through Cacheable或者Non-cacheable,那么hardware translation table walk将直接和external main memory。

6、维护TLB的指令

我们将在下一章,配合linux的标准TLB flush接口来描述。

四、TLB flush API

和TLB flush操作相关的接口API主要包括:

1、 void flush_tlb_all(void)。

这个接口用来invalidate TLB cache中的所有的条目。执行完毕了该接口之后,由于TLB cache中没有缓存任何的VA到PA的转换信息,因此,调用该接口API之前的所有的对page table的修改都可以被CPU感知到。注:该接口是大杀器,不要随便使用。

对于ARM64,flush_tlb_all接口使用的底层命令是:tlbi vmalle1is。Tlbi是TLB Invalidate指令,vmalle1is是参数,指明要invalidate那些TLB。vm表示本次invalidate操作对象是当前VMID,all表示要invalidate所有的TLB entry,e1是表示要flush的TLB entry的memory space identifier是EL0和EL1,regime stage 1的TLB entry。is是inner shareable的意思,表示要invalidate所有inner shareable内的所有PEs的TLB。如果没有is,则表示要flush的是local TLB,其他processor core的TLB则不受影响。

flush_tlb_all接口有一个变种:local_flush_tlb_all。flush_tlb_all是invalidate系统中所有的TLB(各个PEs上的TLB),而local_flush_tlb_all仅仅是invalidate本CPU core上的TLB。local_flush_tlb_all对应的底层接口是:tlbi vmalle1,没有is参数。

2、 void flush_tlb_mm(struct mm_struct *mm)。

这个接口用来invalidate TLB cache中所有和mm这个进程地址空间相关的条目。执行完毕了该接口之后,由于TLB cache中没有缓存任何的mm地址空间中VA到PA的转换信息,因此,调用该接口API之前的所有的对mm地址空间的page table的修改都可以被CPU感知到。

对于ARM64,flush_tlb_mm接口使用的底层命令是:tlbi aside1is, asid。is是inner shareable的意思,表示该操作要广播到inner shareable domain中的所有PEs。asid表示该操作范围是根据asid来进行的。

3、void flush_tlb_page(struct vm_area_struct *vma, unsigned long addr)。

flush_tlb_page接口函数对addr对应的TLB entry进行flush操作。这个函数类似于flush_tlb_range,只不过flush_tlb_range操作了一片VA区域,涉及若干TLB entry,而flush_tlb_page对range进行了限定(range的size就是一个page),因此,也就只是invalidate addr对应的tlb entry。

对于ARM64,flush_tlb_page接口使用的底层命令是:tlbi vale1is, addr。va参数是virtual address的意思,表示本次flush tlb的操作是针对当前asid中的某个virtual address而进行的,addr给出具体要操作的地址和ASID信息。l表示last level,也就是说用户修改了last level Translation table的内容(一般而言,PTE就是last level,在某些情况下,例如section map,last level是PMD),那么我们仅仅需要flush最后一级的页表(page table walk可以有多级)。

4、 void flush_tlb_range(struct vm_area_struct *vma, unsigned long start, unsigned long end)。

flush_tlb_range接口是一个flush强度比flush_tlb_mm要弱的接口,flush_tlb_range不是invalidate整个地址空间的TBL,而是针对该地址空间中的一段虚拟内存(start到end-1)在TLB中的entry进行flush。

ARM64并没有直接flush一个range的硬件操作接口,因此,在ARM64的代码中,flush一个range是通过flush一个个的page来实现的。

五、参考文献

1、Cortex A72 TRM

2、ARM ARM

3、Documentation/cachetlb.txt

原文地址:https://www.cnblogs.com/alantu2018/p/8447573.html

时间: 2024-10-29 03:41:04

TLB flush操作的相关文章

HRegionServer Flush操作源码分析

Flush操作是将HBase中的数据存到硬盘上的过程,具体的flush的流程图如下,本文主要简要分析flush的过程相关源码. Flush 任务提交 每当HRegion完成数据插入的操作的时候,就会进行检查此时是否需要进行一次flush,flush是将HRegion缓存的数据存储到磁盘的过程: long addedSize = doMiniBatchMutation(batchOp); long newSize = this.addAndGetGlobalMemstoreSize(addedSi

Elasticsearch的Refresh与Flush操作

初次接触到这两个概念,估计都会觉得他们没什么差别,都是为了在操作索引之后让索引可以被实时性的搜索,不过它们还是有点不同的.Elasticsearch底层依赖Lucene,这里我们介绍下Lucene的segment, Reopen,commit.Segment在ES中,基本的存储单元是shard(分片),但是在更底层的Lucene上稍微有点不同,ES的每一个shard是Lucene的一个index(索引),Lucene的索引由多个segment组成,每个segment就是ES文档的倒序索引,里面包

当redis执行flush操作后的补救措施

redis误操作flushall的补救措施前提:开启了aof持久化注意: 当执行了flushall或者flushdb等手残命令,切记千万不要触发aof重写 可以利用config set命令将如下两值调巨大,防止重写操作.auto-aof-rewrite-percentage 100auto-aof-rewrite-min-size 64mb 手残操作:由于我这个远远没有达到aof重写,所以没有设置那两个参数 然后打开aof文件发现其实就是多了3行记录这时候把红框中的三行删掉并保存退出 保险起见,

Linux进程切换(2) TLB处理

一.前言 进程切换是一个复杂的过程,本文不准备详细描述整个进程切换的方方面面,而是关注进程切换中一个小小的知识点:TLB的处理.为了能够讲清楚这个问题,我们在第二章描述在单CPU场景下一些和TLB相关的细节,第三章推进到多核场景,至此,理论部分结束.在第二章和第三章,我们从基本的逻辑角度出发,并不拘泥于特定的CPU和特定的OS,这里需要大家对基本的TLB的组织原理有所了解,具体可以参考本站的<TLB操作>一文.再好的逻辑也需要体现在HW block和SW block的设计中,在第四章,我们给出

HBase – Memstore Flush和flush shell操作 深度解析

//memstore flush机制 和flush shell命令刷新//Memstore是HBase框架中非常重要的组成部分之一,是HBase能够实现高性能随机读写至关重要的一环.深入理解Memstore的工作原理.运行机制以及相关配置,对hbase集群管理.性能调优都有着非常重要的帮助. 写机制(大约)1.HBase是基于LSM-Tree模型的,2.所有的数据更新插入操作都首先写入Memstore中(同时会顺序写到日志HLog中),3.达到指定大小之后再将这些修改操作批量写入磁盘,生成一个新

HBase 手动 flush 机制梳理

对应 HBase 版本0.94.1,对照了开源的版本和工作使用的某发行版 问题:在 HBase shell 里面输入 flush 'table_or_region_name'之后,发生了什么?具体的实现是怎么样的?对于现有的某个表,我如何在做操作之前估算 flush 执行的时间? 1. HBase shell 入口 HBase shell 使用 ruby 实现,在 putty 敲hbase shell,调用的是${HBASE_HOME}/bin/hbase这个 bash 脚本,根据shell这个

POI操作EXCEL(二)

原文转自:http://www.tqcto.com/article/code/295025.html java当初把核心处理设成Unicode,带来的好处是另代码适应了多语言环境.然而由于老外的英语只有26个字母,有些情况下,一些程序员用8 位的byte处理,一不小心就去掉了CJK的高位.或者是由于习惯在程序中采用硬编码,还有多种原因,使得许多java应用在CJK的处理上很烦恼.还好 在POI HSSF中考虑到这个问题,可以设置encoding为双字节. POI可以到www.apache.org

关于hbase的read操作的深入研究 region到storefile过程

这里面说的read既包括get,也包括scan,实际底层来看这两个操作也是一样的.我们将要讨论的是,当我们从一张表读取数据的时候hbase到底是怎么处理的.分二种情况来看,第一种就是表刚创建,所有put的数据还在memstore中,并没有刷新到hdfs上:第二种情况是,该store已经进行多次的flush操作,产生了多个storefile了.在具体说明两种情况前,先考虑下表的region的问题,如果表只有一个region,那么没有说的,肯定是要扫描这个唯一的region.假设该表有多个regio

MySQL Flush导致的等待问题

--MySQL Flush导致的等待问题 -------------------------------2014/07/13 前言 在实际生产环境中有时会发现大量的sql语句处于waiting for table.. 状态中,有时候看起来会让人觉得困惑,本文将讲解此状态产生的原因及解决办法. 正文 本文中用到了lock table来模拟实际情况, 首先介绍一个lock tables需要注意的事项,如果在一个session中使用了lock tables,那么在此session中将只能访问获取到lo