22

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

64 位段基础的上下文切换相当昂贵。 通过使用 modify_ldt(2) 设置 LDT 或在内核 2.5 或更高版本中使用 set_thread_area(2) 系统调用,使用段选择器设置 32 位基数可能是更快的替代方法。仅当您要设置大于 4GB 的基础时才需要 arch_prctl()。可以通过使用带有 MAP_32BIT 标志的 mmap(2) 来分配前 2GB 地址空间中的内存。

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

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

4

1 回答 1

3

哇,这个问题是在 12 月问的,没人回答吗?其中一些您可能已经知道,如果知道,我深表歉意。

只是因为执行 wrmsr 的步骤很慢。在任务切换时只加载段寄存器更容易和更快。

在非常现代的 Intel 处理器上,“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 基础来切换线程。这要求 TEB 低于 Linux 的 arch_prctl 的性能说明中的 2^32 - 否则,用户模式调度将需要在每次切换到不同的线程时调用 NT 内核以执行 wrmsr,从而破坏了整点用户模式调度。

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

于 2014-06-14T18:57:54.337 回答