标签:共享内存区 目的 影响 summary ati 寄存器 jvm 多进程 2.0
LinuxThreads 项目最初将多线程的概念引入了 Linux?,可是 LinuxThreads 并不遵守 POSIX 线程标准。虽然更新的 Native POSIX Thread Library(NPTL)库填补了一些空白,可是这仍然存在一些问题。
本文为那些须要将自己的应用程序从 LinuxThreads 移植到 NPTL 上或者仅仅是希望理解有何差别的开发者介绍这两种 Linux 线程模型之间的差别。
当 Linux 最初开发时。在内核中并不能真正支持线程。
可是它的确能够通过 clone()
系统调用将进程作为可调度的实体。
这个调用创建了调用进程(calling process)的一个拷贝,这个拷贝与调用进程共享同样的地址空间。
LinuxThreads 项目使用这个调用来全然在用户空间模拟对线程的支持。不幸的是。这样的方法有一些缺点。尤其是在信号处理、调度和进程间同步原语方面都存在问题。另外,这个线程模型也不符合 POSIX 的要求。
要改进 LinuxThreads。很明显我们须要内核的支持。而且须要重写线程库。有两个相互竞争的项目開始来满足这些要求。一个包含 IBM 的开发者的团队开展了 NGPT(Next-Generation POSIX Threads)项目。同一时候。Red Hat 的一些开发者开展了 NPTL 项目。NGPT 在 2003 年中期被放弃了,把这个领域全然留给了 NPTL。
虽然从 LinuxThreads 到 NPTL 看起来似乎是一个必定的过程。可是假设您正在为一个历史悠久的 Linux 发行版维护一些应用程序,而且计划非常快就要进行升级,那么怎样迁移到 NPTL 上就会变成整个移植过程中重要的一个部分。另外,我们可能会希望了解二者之间的差别,这样就行对自己的应用程序进行设计。使其可以更好地利用这两种技术。
本文具体介绍了这些线程模型各自是在哪些发行版上实现的。
线程 将应用程序划分成一个或多个同一时候执行的任务。线程与传统的多任务进程 之间的差别在于:线程共享的是单个进程的状态信息,并会直接共享内存和其它资源。
同一个进程中线程之间的上下文切换通常要比进程之间的上下文切换速度更快。因此。多线程程序的长处就是它能够比多进程应用程序的执行速度更快。
另外,使用线程我们能够实现并行处理。这些相对于基于进程的方法所具有的长处推动了 LinuxThreads 的实现。
LinuxThreads 最初的设计相信相关进程之间的上下文切换速度非常快,因此每一个内核线程足以处理非常多相关的用户级线程。这就导致了一对一 线程模型的革命。
让我们来回想一下 LinuxThreads 设计细节的一些基本理念:
LinuxThreads 很出名的一个特性就是管理线程(manager thread)。管理线程能够满足下面要求:
pthread_exit()
,那么这个线程就无法结束。主线程要进入睡眠状态,而管理线程的工作就是在全部线程都被杀死之后来唤醒这个主线程。LinuxThreads 接收到终止信号之后,管理线程就会使用同样的信号杀死全部其它线程(进程)。
LinuxThreads 及其局限性
LinuxThreads 的设计通常都能够非常好地工作;可是在压力非常大的应用程序中,它的性能、可伸缩性和可用性都会存在问题。
以下让我们来看一下 LinuxThreads 设计的一些局限性:
因此,这并不遵守 POSIX 中处理信号的方法。
kill()
所发送的信号被传递到一些单独的线程,而不是集中总体进行处理。这意味着假设有线程堵塞了这个信号,那么 LinuxThreads 就仅仅能对这个线程进行排队,并在线程开放这个信号时在运行处理,而不是像其它没有堵塞信号的线程中一样马上处理这个信号。
setuid()
/setgid()
进程对于不同的线程来说可能都是不同的。按需增长(grow on demand) 的概念(也称为浮动堆栈的概念)是在 2.4.10 版本号的 Linux 内核中实现的。在此之前,LinuxThreads 使用的是固定堆栈。
NPTL。或称为 Native POSIX Thread Library。是 Linux 线程的一个新实现,它克服了 LinuxThreads 的缺点,同一时候也符合 POSIX 的需求。
与 LinuxThreads 相比,它在性能和稳定性方面都提供了重大的改进。
与 LinuxThreads 一样。NPTL 也实现了一对一的模型。
Ulrich Drepper 和 Ingo Molnar 是 Red Hat 參与 NPTL 设计的两名员工。他们的整体设计目标例如以下:
LD_ASSUME_KERNEL
,这会在本文稍后进行讨论。
与 LinuxThreads 相比,NPTL 具有非常多长处:
它甚至还通过在清除父线程之前进行等待,从而实现对全部线程结束的管理,这样能够避免僵尸进程的问题。
为了这个目的,NPTL 引入了一种名为 futex 的新机制。futex 在共享内存区域上进行工作,因此能够在进程之间进行共享,这样就能够提供进程间 POSIX 同步机制。我们也能够在进程之间共享一个 futex。这样的行为使得进程间同步成为可能。实际上,NPTL
包括了一个 PTHREAD_PROCESS_SHARED
宏,使得开发者能够让用户级进程在不同进程的线程之间共享相互排斥锁。
getpid()
会为全部的线程返回同样的进程
ID。比如。假设发送了 SIGSTOP
信号。那么整个进程都会停止;使用
LinuxThreads,仅仅有接收到这个信号的线程才会停止。这样能够在基于 NPTL 的应用程序上更好地利用调试器,比如 GDB。这帮助实现了与 LinuxThreads 的向后兼容性。这个特性是通过使用 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 模型来设计的,那么全部这些设置通常都能够使用。
大部分现代 Linux 发行版都预装了 LinuxThreads 和 NPTL,因此它们提供了一种机制来在二者之间进行切换。要查看您的系统上正在使用的是哪个线程库。请执行以下的命令:
$ getconf GNU_LIBPTHREAD_VERSION
这会产生类似于以下的输出结果:
NPTL 0.34
或者:
linuxthreads-0.10
表 1 列出了一些流行的 Linux 发行版,以及它们所採用的线程实现的类型、glibc 库和内核版本号。
线程实现 | C 库 | 发行版 | 内核 |
---|---|---|---|
LinuxThreads 0.7, 0.71 (for libc5) | libc 5.x | Red Hat 4.2 | |
LinuxThreads 0.7, 0.71 (for glibc 2) | glibc 2.0.x | Red Hat 5.x | |
LinuxThreads 0.8 | glibc 2.1.1 | Red Hat 6.0 | |
LinuxThreads 0.8 | glibc 2.1.2 | Red Hat 6.1 and 6.2 | |
LinuxThreads 0.9 | Red Hat 7.2 | 2.4.7 | |
LinuxThreads 0.9 | glibc 2.2.4 | Red Hat 2.1 AS | 2.4.9 |
LinuxThreads 0.10 | glibc 2.2.93 | Red Hat 8.0 | 2.4.18 |
NPTL 0.6 | glibc 2.3 | Red Hat 9.0 | 2.4.20 |
NPTL 0.61 | glibc 2.3.2 | Red Hat 3.0 EL | 2.4.21 |
NPTL 2.3.4 | glibc 2.3.4 | Red Hat 4.0 | 2.6.9 |
LinuxThreads 0.9 | glibc 2.2 | SUSE Linux Enterprise Server 7.1 | 2.4.18 |
LinuxThreads 0.9 | glibc 2.2.5 | SUSE Linux Enterprise Server 8 | 2.4.21 |
LinuxThreads 0.9 | glibc 2.2.5 | United Linux | 2.4.21 |
NPTL 2.3.5 | glibc 2.3.3 | SUSE Linux Enterprise Server 9 | 2.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 上还有很多其它工作要做才干使它具有更好的可伸缩性,从而满足高端应用程序的需求。
标签:共享内存区 目的 影响 summary ati 寄存器 jvm 多进程 2.0
原文地址:http://www.cnblogs.com/clnchanpin/p/7145416.html