ARM中的Thread ID寄存器(如TPIDR_EL0/TPIDR_EL1)的目的是什么?

6
根据ARM文档,像TPIDR_EL0TPIDR_EL1这样的线程ID寄存器,
提供了存储软件线程和进程ID以进行操作系统管理的位置。这些寄存器对处理器行为没有影响。
为什么有人想要将线程ID存储在特殊寄存器中?ARM处理器是否需要像MMU一样在内存中拥有线程的特殊结构?对于ARM来说,线程是一种特殊的东西,ARM希望在某个地方找到它吗?或者我可以完全不使用这个寄存器来实现(高效的)线程?
我之所以问这个问题,是因为我在Fuchsia OS的Zircon Kernel中发现了这段代码:
static inline void arch_set_current_thread(Thread* t) {
  __arm_wsr64("tpidr_el1", (uint64_t)&t->arch_.thread_pointer_location);
  __isb(ARM_MB_SY);
}

系统启动时会创建一个线程,并将其指针存储在 tpidr_el1 中。


3
操作系统需要记住当前在给定(逻辑)CPU 上运行的线程/进程。它需要一个只能在特权模式下更改并可以指定内存区域的 CPU 寄存器。在 x86 上是 fsgs 寄存器,在 ARM 上为 TPIDR_ELx。每个 CPU 需要一块内存区域,因为内核代码被设计为在任何 CPU 上平等运行(因此代码相同但指针不同)。 - Margaret Bloom
@MargaretBloom 的话完全有道理,谢谢。 - Guerlando OCs
1
线程本地数据可以通过EL1进行索引。就像“PIC”一样,您可以拥有“静态基础”代码。其中所有全局变量都相对于固定寄存器引用。线程的实现类似于“静态基础”。但是,进程中的所有线程都可以使用绝对寻址来访问全局变量(在线程之间共享),但它们使用EL1来访问“线程本地”变量。切换线程只涉及更改此寄存器(可以在用户空间中完成),但操作系统需要记录活动线程的上下文切换。当涉及到虚拟化程序时,必须进行陷阱处理。 - artless noise
1个回答

7

所有与用户空间中线程本地存储相关的内容都需要保存在每个线程结构中。您需要在某个地方保留此结构的地址。在armv8中,TPIDR_EL0可用于此目的。在x86_64中,通常会重新分配fs段寄存器以供此用途。

Fuchsia的线程本地存储ABI在其网站上有文档记录。

在Fuchsia中,TPIDR_EL0将为您获取pthread结构。请参见__allocate_thread以了解如何分配某些内存。
一个用法示例(除了线程本地变量之外)是SafeStack功能,它在pthread结构中存储第二个堆栈指针。
在armv8架构下,内核使用TPIDR_EL1来承载一个指针用于保存内核线程结构。需要注意的是,在armv8中,存在用于EL0(用户空间)和EL1(内核空间)的寄存器。而在x86-64架构下,则不存在类似的分离方式,处理起来有些棘手:内核有一个内部存储区用于存储gs寄存器的“内核版本”,并使用swapgs指令在用户空间和内核空间之间切换gs寄存器。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接