UNIX系统从内核态切换到用户态时是否会执行TLB flush?
系统调用返回用户态时的TLB刷新机制解答
先说明确结论:
无特殊安全加固配置的前提下,getpid()这类普通系统调用从内核态返回用户态时,不会执行TLB flush。
具体原因和边界场景说明如下:
- 硬件层面天然有权限隔离机制:现代CPU的MMU会在页表项中标记权限位,内核空间的页表项会被标记为仅超级用户(内核态)可访问,用户态下即使TLB中存在内核地址的映射条目,CPU尝试访问这些地址也会直接触发异常,不需要通过刷新TLB来阻止用户进程访问内核地址空间。
- 主流操作系统都会做性能优化:系统调用是非常高频的操作,如果每次返回都刷新TLB,会导致后续访问用户地址、以及下一次系统调用访问内核地址时都要重新加载TLB条目,会带来非常大的性能损耗,因此默认会保留内核地址的TLB条目,避免不必要的刷新。
- 仅两类场景会在系统调用返回时触发TLB刷新:
- 本次系统调用修改了当前进程的地址空间映射,比如
mmap、munmap、mprotect这类和内存映射相关的系统调用,返回时需要刷新对应地址范围的TLB,保证映射修改生效。 - 系统开启了针对CPU侧信道漏洞的缓解机制,比如针对熔断漏洞的KPTI(内核页表隔离)。开启KPTI后,内核和用户态会使用独立的页表,系统调用返回用户态时会刷新TLB中内核地址的相关条目,避免攻击者通过侧信道泄露内核地址信息。
- 本次系统调用修改了当前进程的地址空间映射,比如
- 常规的进程调度场景下才会执行全量/部分TLB刷新:当CPU从一个进程切换到另一个进程时,需要清空旧进程的用户地址空间TLB条目,避免新进程访问到旧进程的地址映射。现在支持PCID(进程上下文标识符)的CPU还可以进一步优化,给不同进程的TLB条目打不同的标识,不需要每次切换都全量刷新TLB。
内容的提问来源于stack exchange,提问作者Asurii
相关产品推荐
相关产品推荐

