首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

Linux 线程模型的比较:LinuxThreads 和 NPTL(2)

Linux 线程模型的比较:LinuxThreads 和 NPTL(2)

关于 NPTLNPTL,或称为 Native POSIX Thread Library,是 Linux 线程的一个新实现,它克服了 LinuxThreads 的缺点,同时也符合 POSIX 的需求。与 LinuxThreads 相比,它在性能和稳定性方面都提供了重大的改进。与 LinuxThreads 一样,NPTL 也实现了一对一的模型。
Ulrich Drepper 和 Ingo Molnar 是 Red Hat 参与 NPTL 设计的两名员工。他们的总体设计目标如下:
  • 这个新线程库应该兼容 POSIX 标准。
  • 这个线程实现应该在具有很多处理器的系统上也能很好地工作。
  • 为一小段任务创建新线程应该具有很低的启动成本。
  • NPTL 线程库应该与 LinuxThreads 是二进制兼容的。注意,为此我们可以使用 LD_ASSUME_KERNEL,这会在本文稍后进行讨论。
  • 这个新线程库应该可以利用 NUMA 支持的优点。
NPTL 的优点与 LinuxThreads 相比,NPTL 具有很多优点:
  • NPTL 没有使用管理线程。管理线程的一些需求,例如向作为进程一部分的所有线程发送终止信号,是并不需要的;因为内核本身就可以实现这些功能。内核还会处理每个线程堆栈所使用的内存的回收工作。它甚至还通过在清除父线程之前进行等待,从而实现对所有线程结束的管理,这样可以避免僵尸进程的问题。
  • 由于 NPTL 没有使用管理线程,因此其线程模型在 NUMA 和 SMP 系统上具有更好的可伸缩性和同步机制。
  • 使用 NPTL 线程库与新内核实现,就可以避免使用信号来对线程进行同步了。为了这个目的,NPTL 引入了一种名为 futex 的新机制。futex 在共享内存区域上进行工作,因此可以在进程之间进行共享,这样就可以提供进程间 POSIX 同步机制。我们也可以在进程之间共享一个 futex。这种行为使得进程间同步成为可能。实际上,NPTL 包含了一个 PTHREAD_PROCESS_SHARED 宏,使得开发人员可以让用户级进程在不同进程的线程之间共享互斥锁。
  • 由于 NPTL 是 POSIX 兼容的,因此它对信号的处理是按照每进程的原则进行的;getpid() 会为所有的线程返回相同的进程 ID。例如,如果发送了 SIGSTOP 信号,那么整个进程都会停止;使用 LinuxThreads,只有接收到这个信号的线程才会停止。这样可以在基于 NPTL 的应用程序上更好地利用调试器,例如 GDB。
  • 由于在 NPTL 中所有线程都具有一个父进程,因此对父进程汇报的资源使用情况(例如 CPU 和内存百分比)都是对整个进程进行统计的,而不是对一个线程进行统计的。
  • NPTL 线程库所引入的一个实现特性是对 ABI(应用程序二进制接口)的支持。这帮助实现了与 LinuxThreads 的向后兼容性。这个特性是通过使用 LD_ASSUME_KERNEL 实现的,下面就来介绍这个特性。
LD_ASSUME_KERNEL 环境变量正如上面介绍的一样,ABI 的引入使得可以同时支持 NPTL 和 LinuxThreads 模型。基本上来说,这是通过 ld (一个动态链接器/加载器)来进行处理的,它会决定动态链接到哪个运行时线程库上。
举例来说,下面是 WebSphere® Application Server 对这个变量所使用的一些通用设置;您可以根据自己的需要进行适当的设置:
  • LD_ASSUME_KERNEL=2.4.19:这会覆盖 NPTL 的实现。这种实现通常都表示使用标准的 LinuxThreads 模型,并启用浮动堆栈的特性。
  • LD_ASSUME_KERNEL=2.2.5:这会覆盖 NPTL 的实现。这种实现通常都表示使用 LinuxThreads 模型,同时使用固定堆栈大小。
我们可以使用下面的命令来设置这个变量:
export LD_ASSUME_KERNEL=2.4.19
注意,对于任何 LD_ASSUME_KERNEL 设置的支持都取决于目前所支持的线程库的 ABI 版本。例如,如果线程库并不支持 2.2.5 版本的 ABI,那么用户就不能将 LD_ASSUME_KERNEL 设置为 2.2.5。通常,NPTL 需要 2.4.20,而 LinuxThreads 则需要 2.4.1。
如果您正运行的是一个启用了 NPTL 的 Linux 发行版,但是应用程序却是基于 LinuxThreads 模型来设计的,那么所有这些设置通常都可以使用。
GNU_LIBPTHREAD_VERSION 宏大部分现代 Linux 发行版都预装了 LinuxThreads 和 NPTL,因此它们提供了一种机制来在二者之间进行切换。要查看您的系统上正在使用的是哪个线程库,请运行下面的命令:
$ getconf GNU_LIBPTHREAD_VERSION
这会产生类似于下面的输出结果:
NPTL 0.34
或者:
linuxthreads-0.10
Linux 发行版所使用的线程模型、glibc 版本和内核版本表 1 列出了一些流行的 Linux 发行版,以及它们所采用的线程实现的类型、glibc 库和内核版本。
表 1. Linux 发行版及其线程实现线程实现C 库发行版内核LinuxThreads 0.7, 0.71 (for libc5)libc 5.xRed Hat 4.2
LinuxThreads 0.7, 0.71 (for glibc 2)glibc 2.0.xRed Hat 5.x
LinuxThreads 0.8glibc 2.1.1Red Hat 6.0
LinuxThreads 0.8glibc 2.1.2Red Hat 6.1 and 6.2
LinuxThreads 0.9
Red Hat 7.22.4.7LinuxThreads 0.9glibc 2.2.4Red Hat 2.1 AS2.4.9LinuxThreads 0.10glibc 2.2.93Red Hat 8.02.4.18NPTL 0.6glibc 2.3Red Hat 9.02.4.20NPTL 0.61glibc 2.3.2Red Hat 3.0 EL2.4.21NPTL 2.3.4glibc 2.3.4Red Hat 4.02.6.9LinuxThreads 0.9glibc 2.2SUSE Linux Enterprise Server 7.12.4.18LinuxThreads 0.9glibc 2.2.5SUSE Linux Enterprise Server 82.4.21LinuxThreads 0.9glibc 2.2.5United Linux2.4.21NPTL 2.3.5glibc 2.3.3SUSE Linux Enterprise Server 92.6.5
注意,从 2.6.x 版本的内核和 glibc 2.3.3 开始,NPTL 所采用的版本号命名约定发生了变化:这个库现在是根据所使用的 glibc 的版本进行编号的。
Java™ 虚拟机(JVM)的支持可能会稍有不同。IBM 的 JVM 可以支持表 1 中 glibc 版本高于 2.1 的大部分发行版。
结束语LinuxThreads 的限制已经在 NPTL 以及 LinuxThreads 后期的一些版本中得到了克服。例如,最新的 LinuxThreads 实现使用了线程注册来定位线程本地数据;例如在 Intel® 处理器上,它就使用了 %fs 和 %gs 段寄存器来定位访问线程本地数据所使用的虚拟地址。尽管这个结果展示了 LinuxThreads 所采纳的一些修改的改进结果,但是它在更高负载和压力测试中,依然存在很多问题,因为它过分地依赖于一个管理线程,使用它来进行信号处理等操作。
您应该记住,在使用 LinuxThreads 构建库时,需要使用 -D_REENTRANT 编译时标志。这使得库线程是安全的。
最后,也许是最重要的事情,请记住 LinuxThreads 项目的创建者已经不再积极更新它了,他们认为 NPTL 会取代 LinuxThreads。
LinuxThreads 的缺点并不意味着 NPTL 就没有错误。作为一个面向 SMP 的设计,NPTL 也有一些缺点。我曾经看到过在最近的 Red Hat 内核上出现过这样的问题:一个简单线程在单处理器的机器上运行良好,但在 SMP 机器上却挂起了。我相信在 Linux 上还有更多工作要做才能使它具有更好的可伸缩性,从而满足高端应用程序的需求。
返回列表