Linux线程比较:LinuxThreads 和NPTL( 二 )


;根据 LinuxThreads 的设计,如果一个异步信号被发送了,那么管理线程就会将这个信号发送给一个线程 。如果这个线程现在阻塞了这个信号,那么这个信号也就会被挂起 。这是因为管理线程无法将这个信号发送给进程;相反,每个线程都是作为一个进程在执行 。
;线程之间的调度是由内核调度器来处理的 。LinuxThreads 及其局限性
LinuxThreads 的设计通常都可以很好地工作;但是在压力很大的应用程序中,它的性能、可伸缩性和可用性都会存在问题 。下面让我们来看一下 LinuxThreads 设计的一些局限性:
它使用管理线程来创建线程,并对每个进程所拥有的所有线程进行协调 。这增加了创建和销毁线程所需要的开销 。
;由于它是围绕一个管理线程来设计的,因此会导致很多的上下文切换的开销,这可能会妨碍系统的可伸缩性和性能 。
;由于管理线程只能在一个 CPU 上运行,因此所执行的同步操作在 SMP 或 NUMA 系统上可能会产生可伸缩性的问题 。
;由于线程的管理方式,以及每个线程都使用了一个不同的进程 ID,因此 LinuxThreads 与其他与 POSIX 相关的线程库并不兼容 。
;信号用来实现同步原语,这会影响操作的响应时间 。另外,将信号发送到主进程的概念也并不存在 。因此,这并不遵守 POSIX 中处理信号的方法 。
;LinuxThreads 中对信号的处理是按照每线程的原则建立的,而不是按照每进程的原则建立的,这是因为每个线程都有一个独立的进程 ID 。由于信号被发送给了一个专用的线程,因此信号是串行化的 ―― 也就是说,信号是透过这个线程再传递给其他线程的 。这与 POSIX 标准对线程进行并行处理的要求形成了鲜明的对比 。例如,在 LinuxThreads 中,通过 kill() 所发送的信号被传递到一些单独的线程,而不是集中整体进行处理 。这意味着如果有线程阻塞了这个信号,那么 LinuxThreads 就只能对这个线程进行排队,并在线程开放这个信号时在执行处理,而不是像其他没有阻塞信号的线程中一样立即处理这个信号 。
;由于 LinuxThreads 中的每个线程都是一个进程,因此用户和组 ID 的信息可能对单个进程中的所有线程来说都不是通用的 。例如,一个多线程的 setuid()/setgid() 进程对于不同的线程来说可能都是不同的 。
;有一些情况下,所创建的多线程核心转储中并没有包含所有的线程信息 。同样,这种行为也是每个线程都是一个进程这个事实所导致的结果 。如果任何线程发生了问题,我们在系统的核心文件中只能看到这个线程的信息 。不过,这种行为主要适用于早期版本的 LinuxThreads 实现 。
;由于每个线程都是一个单独的进程,因此 /proc 目录中会充满众多的进程项,而这实际上应该是线程 。
;由于每个线程都是一个进程,因此对每个应用程序只能创建有限数目的线程 。例如,在 IA32 系统上,可用进程总数 ―― 也就是可以创建的线程总数 ―― 是 4,090 。
;由于计算线程本地数据的方法是基于堆栈地址的位置的,因此对于这些数据的访问速度都很慢 。另外一个缺点是用户无法可信地指定堆栈的大小,因为用户可能会意外地将堆栈地址映射到本来要为其他目的所使用的区域上了 。按需增长(grow on demand) 的概念(也称为浮动堆栈 的概念)是在 2.4.10 版本的 Linux 内核中实现的 。在此之前,LinuxThreads 使用的是固定堆栈 。关于 NPTL
NPTL,或称为 Native POSIX Thread Library,是 Linux 线程的一个新实现,它克服了 LinuxThreads 的缺点,同时也符合 POSIX 的需求 。与 LinuxThreads 相比,它在性能和稳定性方面都提供了重大的改进 。与 LinuxThreads 一样,NPTL 也实现了一对一的模型 。

推荐阅读