上下文切换对64位段基的性能影响

我对arch_prctl(2)的手册页中的措辞感到困惑。 具体来说,它说:

64位段库的上下文切换相当昂贵。 在内核2.5或更高版本中,使用modify_ldt(2)设置LDT或使用set_thread_area(2)系统调用,可能会更快地使用段select器来设置32位基址。 仅当您要设置大于4GB的基底时才需要arch_prctl()。 第一个2GB地址空间中的内存可以通过使用mmap(2)和MAP_32BIT标志来分配。

这是否意味着使用此系统调用的进程的上下文切换将收到性能损失或具体含义?

查看Linux内核的源代码后,看起来对于<4 GiB的地址使用LDT,而> 4 GiB地址使用模型特定的寄存器。

来自do_arch_prctl

 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的价格只在进程之间切换时支付,这意味着当没有其他进程计划运行时,通过系统调用进入内核没有额外的成本。

哇,十二月有人问这个问题,没人回答吗? 其中一些你可能已经知道了,如果是的话,我表示歉意。

这只是因为做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中的用户模式调度是通过为每个可调度线程创建一个指向TLS块(在Windows中称为“线程环境块”或TEB)段的LDT来实现的。 除了上下文切换之外,用户模式还可以通过重新加载GS库来切换线程。 这要求TEBs低于Linux的arch_prctl性能注释中的2 ^ 32,否则,用户模式调度每次切换到不同的线程时都需要调用NT内核,以便执行wrmsr,从而击败整个点的用户模式调度。

在Windows 8.1中,添加了对wrgsbase的支持,并且还为用户模式启用了它。 8.1中的用户模式调度使用wrgsbase而不是GS段重新加载和LDT(如果CPU具有它)。