news 2026/5/23 17:40:46

Linux 内存管理:TLB ASID

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Linux 内存管理:TLB ASID

文章目录

  • 1. 前言
  • 2. TLB ASID 的硬件支持
    • 2.1 概念
    • 2.2 TLB 查找
  • 3. Linux 下 TLB ASID 管理
  • 4. 参考资料

1. 前言

限于作者能力水平,本文可能存在谬误,因此而给读者带来的损失,作者不做任何承诺。

2. TLB ASID 的硬件支持

2.1 概念

  • 什么是TLB
    TLBTranslation Lookaside Buffers的缩写,MMU 将虚拟地址(VA)翻译为物理地址(PA)时,要经过页表遍历(page table walk)过程,每访问一级页表就要一次内存访问,相对来说,这个延迟还是相对较大的。为了提高性能,硬件上引入了 TLB cache 缓存,首次访问一个 VA 后,将 MMU 转换的 PA 以对应的 VA 为 tag 缓存到 TLB 缓存,下次再访问同一 VA,就可以通过以 VA 为 tag 从 TLB 搜索提取对应的 TLB entry 了,不用再经历漫长的page table walk过程。当然,TLB 的容量有限,只能缓存有限个数 VA 翻译的 PA 地址,这就需要做一定的管理,在 TLB 耗尽时,通过某种算法,用新的 PA 将一些旧的缓存替换掉。

  • 什么是 TLB ASID?
    ASIDAddress Space Identifier的缩写,标识属于特定进程的TLB entries。那为什么需要它?前面说过,TLB 容量有限,在系统中属于珍贵资源;另外,在系统进程的切换过程中,会进行页表切换,而每个进程的页表不一样,那意味着,当前进程的 TLB 缓存的地址翻译内容,对新进程将失效,所以页表切换需要进行整个 TLB cache 的 flush,这会带来不小的性能损失。于是硬件上引入了nG(not Global)标志位 和ASID,来对该问题进行优化:nG=0标识 TLB entries 属于 ASID 标识的进程,nG=1标识 TLB entries 属于全局的内核空间地址(所有进程共享内核空间的页表映射)。这样,在切换进程的页表时候,只需要为新进程分配一个 ASID 来标识自己的 TLB entries,在 ASID 或 TLB cache entries 消耗完之前,都不需要刷 TLB cache 了。

2.2 TLB 查找

nG标志位同时位于最后一级页表的表项TLB entries中,然后首次内存访问某个地址时,记录到 TLB entries 中。而当前进程的ASID,会在进程页表切换时设置到TTBRx寄存器中,同时也会在首次访问某个VA地址时,记录到TLB entries中。当然,TLB entries 也记录访问VA地址的VA tag。这样,在后续访问某一VA地址时,首先比较 VA 地址 TLB entries VA tag,如果不匹配,则表示 TLB miss,要继续进行page table walk来翻译PA;如果相同,则继续查看 TLB entry 的nG位,如果为nG=0,则意味着是内核空间地址的 TLB entry,所有进程共享,即命中了 TLB,返回 TLB entry 保存的PA即可;如果nG=1,则表示是某进程特定的 TLB entry,通过 ASID 标识,则继续比较 TLB entry 的 ASID 和 TTBRx 寄存器存储的当前进程的 ASID,如果值相同,则标识命中,否则标识 TLB miss,否则要继续进行page table walk来翻译PA

3. Linux 下 TLB ASID 管理

ARMv7架构 +Linux 4.14.x内核简略的分析下TLB ASID的管理细节,来看代码:

typedefstruct{#ifdefCONFIG_CPU_HAS_ASIDatomic64_tid;/* ASID: generation | HW ASID */#else...#endif...}mm_context_t;
voidcheck_and_switch_context(structmm_struct*mm,structtask_struct*tsk){unsignedlongflags;unsignedintcpu=smp_processor_id();u64 asid;...asid=atomic64_read(&mm->context.id);/* 读取新进程的 ASID *//* * a. !((asid ^ atomic64_read(&asid_generation)) >> ASID_BITS * 新进程 asid 属于/分配自 当前 generation * b. atomic64_xchg(&per_cpu(active_asids, cpu), asid) * a.成立的条件下, 顺便设定 当前 CPU 激活的 (进程的) asid, * 然后就可以进入页表的切换过程了. */if(!((asid^atomic64_read(&asid_generation))>>ASID_BITS)&&atomic64_xchg(&per_cpu(active_asids,cpu),asid))gotoswitch_mm_fastpath;/* ASID 无需更新,直接进入页表切换过程 */raw_spin_lock_irqsave(&cpu_asid_lock,flags);/* Check that our ASID belongs to the current generation. */asid=atomic64_read(&mm->context.id);/* 新进程 asid 不 属于/分配自 当前 generation, 要重新从当前 generation 为新进程分配 asid */if((asid^atomic64_read(&asid_generation))>>ASID_BITS){asid=new_context(mm,cpu);/* ASID 管理操作 */atomic64_set(&mm->context.id,asid);/* 为 进程 分配的 ASID 记录到 进程的 mm_struct */}/* 当前 generation ASID 耗尽,需要刷掉所有的 TLB cache */if(cpumask_test_and_clear_cpu(cpu,&tlb_flush_pending)){local_flush_bp_all();local_flush_tlb_all();}atomic64_set(&per_cpu(active_asids,cpu),asid);/* 当前 CPU 激活的 (进程的) asid */cpumask_set_cpu(cpu,mm_cpumask(mm));raw_spin_unlock_irqrestore(&cpu_asid_lock,flags);}staticu64new_context(structmm_struct*mm,unsignedintcpu){staticu32 cur_idx=1;u64 asid=atomic64_read(&mm->context.id);u64 generation=atomic64_read(&asid_generation);/* * 在创建新的进程的时候会分配一个新的 mm, 其(mm->context.id)初始化为 0. * 如果 asid 不等于 0, 那么说明这个 mm 之前就已经分配过 software asid * (generation + hw asid)了. */if(asid!=0){u64 newasid=generation|(asid&~ASID_MASK);/* * If our current ASID was active during a rollover, we * can continue to use it and this was just a false alarm. */if(check_update_reserved_asid(asid,newasid))returnnewasid;/* * We had a valid ASID in a previous life, so try to re-use * it if possible., *//* 如果新 generation 中旧的 asid 还未被分配出去, 重用它 */asid&=~ASID_MASK;if(!__test_and_set_bit(asid,asid_map))returnnewasid;/* 返回更新了 generation 的 asid */}/* * 如果 asid 等于 0, 说明我们的确是需要分配一个新的 HW asid, * 这时候首先要找一个空闲的 HW asid, 如果能够找到, 那么直接返 * 回 software asid (当前 generation + 新分配的 hw asid); 否则, * 表示 asid 消耗完了,生成新的 generation 并重新进行分配??? */asid=find_next_zero_bit(asid_map,NUM_USER_ASIDS,cur_idx);if(asid==NUM_USER_ASIDS){/* ASID 消耗完了,需重新分配 */generation=atomic64_add_return(ASID_FIRST_VERSION,&asid_generation);/* 递增 ASID generation */flush_context(cpu);asid=find_next_zero_bit(asid_map,NUM_USER_ASIDS,1);}__set_bit(asid,asid_map);/* 更新 ASID 分配位图: 标记 asid 已经被分配 */cur_idx=asid;cpumask_clear(mm_cpumask(mm));returnasid|generation;/* 返回新分配的 asid */}staticvoidflush_context(unsignedintcpu){inti;u64 asid;/* Update the list of reserved ASIDs and the ASID bitmap. */bitmap_clear(asid_map,0,NUM_USER_ASIDS);for_each_possible_cpu(i){asid=atomic64_xchg(&per_cpu(active_asids,i),0);/* * If this CPU has already been through a * rollover, but hasn't run another task in * the meantime, we must preserve its reserved * ASID, as this is the only trace we have of * the process it is still running. */if(asid==0)asid=per_cpu(reserved_asids,i);__set_bit(asid&~ASID_MASK,asid_map);per_cpu(reserved_asids,i)=asid;}/* Queue a TLB invalidate and flush the I-cache if necessary. */cpumask_setall(&tlb_flush_pending);/* 当前 generation 的 asid 耗尽, 更新 generation, 标记要刷 TLB */if(icache_is_vivt_asid_tagged())__flush_icache_all();}

4. 参考资料

[1] DDI0406C_d_armv7ar_arm.pdf
[2] TLB原理

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/23 17:40:45

基于微信小程序的数字博物馆系统的设计与实现(程序+文档+讲解)

课题介绍在文博资源数字化、展览体验便捷化需求升级的背景下,传统博物馆存在 “线下参观受限、展品解读单一、互动体验不足” 的痛点,基于微信小程序 SpringBoot 构建的数字博物馆系统,适配游客、博物馆管理员、讲解员等角色,实现…

作者头像 李华
网站建设 2026/5/23 17:40:45

代码随想录算法训练营Day49 | Prim算法、Kruskal算法

Prim算法 53. 寻宝(第七期模拟笔试) 1.思路 本题是最小生成树的模板题,图中有n个节点,那么一定可以用 n-1 条边将所有节点连接到一起,并且总权重最小。 Prim 算法:从一个顶点开始,逐步“生长”…

作者头像 李华
网站建设 2026/5/23 17:40:44

30、Linux软件包管理与源码构建全攻略

Linux软件包管理与源码构建全攻略 在Linux系统中,软件包的管理和源码构建是非常重要的操作。下面将详细介绍DEB文件处理、源码构建以及SRPM安装的相关内容。 1. RPM验证报告字符含义 在RPM验证报告中,有一些特定的字符代表着不同的含义,如下表所示: | 字符 | 含义 | |…

作者头像 李华
网站建设 2026/5/22 20:46:29

Flink Rebalance、Rescale、Shuffle 核心区别

Flink 中的 Rebalance、Rescale、Shuffle 是三种核心的数据流分区策略,用于定义上游算子的并行子任务如何将数据分发到下游算子的并行子任务。三者的核心差异体现在数据分配方式、网络开销、适用场景上,选择不当会直接影响作业的性能和数据分布的均匀性。以下是详细的对比与解…

作者头像 李华
网站建设 2026/5/22 9:50:31

42、Linux编程:软件开发工具探索与实践

Linux编程:软件开发工具探索与实践 1. 编程基础概念 在编程过程中,通常会经历编辑、编译和调试的循环,多数程序员需多次重复此流程,直至程序正确运行。除了掌握这些基本步骤,还需熟悉以下术语和概念: - 变量 :用于存储不同类型的数据,可将其视为数据的占位符,如同…

作者头像 李华