不同进程对应相同的虚拟地址,在 TLB(快表) 是如何区分的?

2023-10-24 09:20

本文主要是介绍不同进程对应相同的虚拟地址,在 TLB(快表) 是如何区分的?,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

大家好,我叫徐锦桐,个人博客地址为www.xujintong.com。平时记录一下学习计算机过程中获取的知识,还有日常折腾的经验,欢迎大家访问。

一、前言

从虚拟地址到物理地址需要进行多级的页表转换,64位操作系统一般是3~5级,如果每次都要进行这个页表转换那会带来性能上的消耗。
如果我们将已知的虚拟地址和物理地址映射关系存到一个缓存表中,那么如果再次用到这个映射关系就能直接查表来找到虚拟地址对应的物理地址了,这就是TLB表,也叫快表。

但是会发生一个歧义,就是两个进程有相同的虚拟地址,但是映射到了不同的物理地址,到时候查表的时候就会发生歧义

二、页表查找简述

我们知道MMU是将虚拟地址转换为物理地址,然后其中的映射关系存储到页表中,而页表又是分级的。


64位操作系统一般有3~5级页表,这里我们以4级页表为例,分别是PGD、PUD、PMD、PTE四级页表。在硬件上有一个页表基地址寄存器,它存储PGD页表的首地址,MMU根据这个寄存器从PGB表开始查,最后查到PTE,然后生成最后的物理地址。


这个就是相当于找你家的具体位置一样,先找你的省份,然后是市级…。当下次再找你家的具体位置时,还要这么来一次,这非常的麻烦。但是如果第一次查完之后,把你的信息记录下来,比如说一个名字对应你家的具体位置,下次找的时候直接查表就行了。但是如果遇到同名的咋办,这就遇到了歧义,解决办法下面我再说。

三、TLB本质

TLB本质就是一个告诉缓存。TLB缓存虚拟地址和其映射的物理地址。


TLB根据虚拟地址查找cache,也只能根据虚拟地址查找。硬件存在TLB后,虚拟地址转到到物理地址的过程发生了变化。


虚拟地址首先传给TLB确认是否命中cache,如果cache hit就直接可以得到其对应的物理地址。否则,就是一级一级的查页表。
TLB1.webp
(操作系统虽然是64位,64位是非常大的一个数,我们目前一般用不多这么多,所以硬件厂商为了设计简单或节约成本,所以64位CPU寻址范围并不是64位,这里以48位的进行举例)

四、解决TLB中的歧义

在进程切换的时候我们可以将整个TLB失效,切换的进程肯定不会命中TLB,但是会导致性能损失,如果我们进程切换的非常频繁,可能TLB表就没咋用。


怎么尽可能避免flush TLB(这里的flush理解成使无效的意思)呢。如果我们能区分不同的进程TLB表就好了。


Linux操作系统为了区分不同的进程,会给每个进程一个独一无二的进程ID,我们也可以除了通过虚拟地址查表外,通过对比一个ID来区分不同进程的TLB表项。


所以,TLB添加一项ASID(Address Space ID)的匹配。ASID就类似进程ID一样,用来区分不同进程的TLB表项。这样在进程切换的时候就不需要flush TLB。但是仍然需要软件管理和分配ASID。
TLB2.webp

五、管理ASID

ASID和进程ID是不一样的。ASID一般是8或16 bit,所以只能区分256或65536个进程,但是进程ID的取值范围非常的大。我们不能将ASID和进程ID一一对应,我们必须给每个进程分配一个ASID。


Linux管理进程,每个进程会有个tast_struct结构,这里存储的进程的基本信息,我们可以把分配给这个进程的ASID存储到这里。页表基地址寄存器如果有空闲位也可以用来存储ASID。


当进程切换时候,可以将页表基地址和ASID(可以从task_struct获得)共同存储在页表基地址寄存器中。当查找TLB的时候,可以对比虚拟地址和ASID和TLB表中的是否一样来区分不同进程TLB表。 如果TLB miss就需要查页表了,然后再缓存到TLB中,同时缓存当前的ASID。

六、up

内核空间和用户空间是分开的,并且内核空间是所有进程共享的。


内核空间是共享的,那么进程A切换进程B的时候,如果进程B访问的地址位于内核空间,我们完全可以用进程A缓存的TLB表。但是现在因为ASID不一样,所以导致TLB miss。


我们针对内核空间这种全局共享的映射关系称为global映射。针对每个进程的映射成为non-global映射。 所以,我们在最后一级页表引入一个bit(non-global(nG)bit)代表是不是global映射。


当映射关系缓存到TLB的时候,将nG bit也缓存下来。这时候,我们的TLB表中有虚拟地址和物理地址的映射关系、ASID、nG bit。当判断是否命中TLB时,先比较虚拟地址,当找到表中的虚拟地址后,然后再看是不是global映射,如果是,就直接TLB hit,这时候就不用比较ASID了。如果不是global映射,最后还需要比较ASID。
TLB3.webp

参考文章:小林coding–面试官:不同进程对应相同的虚拟地址,在 TLB 是如何区分的?

这篇关于不同进程对应相同的虚拟地址,在 TLB(快表) 是如何区分的?的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/274212

相关文章

2. c#从不同cs的文件调用函数

1.文件目录如下: 2. Program.cs文件的主函数如下 using System;using System.Collections.Generic;using System.Linq;using System.Threading.Tasks;using System.Windows.Forms;namespace datasAnalysis{internal static

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

spoj705( 求不相同的子串个数)

题意:求串s的不同子串的个数 解题思路:任何子串都是某个后缀的前缀,对n个后缀排序,求某个后缀的前缀的个数,减去height[i](第i个后缀与第i-1 个后缀有相同的height[i]个前缀)。 代码如下: #include<iostream>#include<algorithm>#include<stdio.h>#include<math.h>#include<cstrin

uva 10061 How many zero's and how many digits ?(不同进制阶乘末尾几个0)+poj 1401

题意是求在base进制下的 n!的结果有几位数,末尾有几个0。 想起刚开始的时候做的一道10进制下的n阶乘末尾有几个零,以及之前有做过的一道n阶乘的位数。 当时都是在10进制下的。 10进制下的做法是: 1. n阶位数:直接 lg(n!)就是得数的位数。 2. n阶末尾0的个数:由于2 * 5 将会在得数中以0的形式存在,所以计算2或者计算5,由于因子中出现5必然出现2,所以直接一

[Linux]:进程(下)

✨✨ 欢迎大家来到贝蒂大讲堂✨✨ 🎈🎈养成好习惯,先赞后看哦~🎈🎈 所属专栏:Linux学习 贝蒂的主页:Betty’s blog 1. 进程终止 1.1 进程退出的场景 进程退出只有以下三种情况: 代码运行完毕,结果正确。代码运行完毕,结果不正确。代码异常终止(进程崩溃)。 1.2 进程退出码 在编程中,我们通常认为main函数是代码的入口,但实际上它只是用户级

速了解MySQL 数据库不同存储引擎

快速了解MySQL 数据库不同存储引擎 MySQL 提供了多种存储引擎,每种存储引擎都有其特定的特性和适用场景。了解这些存储引擎的特性,有助于在设计数据库时做出合理的选择。以下是 MySQL 中几种常用存储引擎的详细介绍。 1. InnoDB 特点: 事务支持:InnoDB 是一个支持 ACID(原子性、一致性、隔离性、持久性)事务的存储引擎。行级锁:使用行级锁来提高并发性,减少锁竞争

MyBatis 切换不同的类型数据库方案

下属案例例当前结合SpringBoot 配置进行讲解。 背景: 实现一个工程里面在部署阶段支持切换不同类型数据库支持。 方案一 数据源配置 关键代码(是什么数据库,该怎么配就怎么配) spring:datasource:name: test# 使用druid数据源type: com.alibaba.druid.pool.DruidDataSource# @需要修改 数据库连接及驱动u

java 进程 返回值

实现 Callable 接口 与 Runnable 相比,Callable 可以有返回值,返回值通过 FutureTask 进行封装。 public class MyCallable implements Callable<Integer> {public Integer call() {return 123;}} public static void main(String[] args

C#关闭指定时间段的Excel进程的方法

private DateTime beforeTime;            //Excel启动之前时间          private DateTime afterTime;               //Excel启动之后时间          //举例          beforeTime = DateTime.Now;          Excel.Applicat

linux中使用rust语言在不同进程之间通信

第一种:使用mmap映射相同文件 fn main() {let pid = std::process::id();println!(