我对arch_prctl(2)
手册页中的措辞感到困惑。具体来说,它声明:
64位段基础的上下文切换相当昂贵。可能 是使用段选择器设置32位基数的更快的替代方法 通过使用modify_ldt(2)或使用set_thread_area(2)设置LDT 内核2.5或更高版本中的系统调用。只有在需要时才需要arch_prctl() 你想设置大于4GB的基数。记忆在第一 使用mmap(2)可以分配2GB的地址空间 MAP_32BIT标志。
这是否意味着使用此系统调用的进程的上下文切换会受到性能损失或具有什么影响?
查看Linux内核的源代码后,对于<4 GiB的地址,使用LDT,而&gt; 4 GiB地址使用特定于模型的寄存器。
case ARCH_SET_FS:
/* handle small bases via the GDT because that's faster to
switch. */
if (addr <= 0xffffffff) {
set_32bit_tls(task, FS_TLS, addr);
if (doit) {
load_TLS(&task->thread, cpu);
loadsegment(fs, FS_TLS_SEL);
}
task->thread.fsindex = FS_TLS_SEL;
task->thread.fs = 0;
} else {
task->thread.fsindex = 0;
task->thread.fs = addr;
if (doit) {
/* set the selector to 0 to not confuse
__switch_to */
loadsegment(fs, 0);
ret = wrmsrl_safe(MSR_FS_BASE, addr);
}
}
put_cpu();
break;
如何使用GDT比写入寄存器更快?此外,我假设更新FS和GS的价格仅在流程之间切换时支付,这意味着当没有其他流程计划运行时,通过系统调用进入内核没有额外成本?
答案 0 :(得分:3)
这只是因为做wrmsr的步骤很慢。在任务切换时加载段寄存器更容易,更快。
在非常现代的英特尔处理器上,添加“rdfsbase”,“wrfsbase”,“rdgsbase”和“wrgsbase”指令允许直接访问FS和GS的基址寄存器,其难度比以前小得多。实际上,内核可以允许它们在用户模式下使用。您可能想检查现代Linux内核是否利用wrfsbase来分配低于4 GB的TLS区域。
我不知道它在Linux上是怎么回事,但从Windows 7开始的Windows NT将用户模式线程调度作为应用程序开发人员的可选功能。与Linux和Mac OS X一样,Windows使用段寄存器的基址(x86-64 Windows中的GS)在x86上实现线程本地存储。此功能类似于光纤,但程序可以用内核识别的方式将其自己的线程上下文切换出来。
Windows中的用户模式调度是通过创建一个LDT来实现的,该LDT的段指向每个可调度线程的TLS块(在Windows中称为“线程环境块”或TEB)。然后,除了上下文切换之外,用户模式还可以通过重新加载GS基础来切换线程。这需要TEB低于2 ^ 32,就像Linux的arch_prctl的性能说明一样 - 否则,用户模式调度每次切换到不同的线程时都需要调用NT内核才能执行wrmsr,从而击败整个点用户模式调度。
在Windows 8.1中,添加了对wrgsbase的支持,并且还为用户模式启用了支持。 8.1中的用户模式调度使用wrgsbase而不是GS段重载,如果CPU有则使用LDT。