亚洲成精品动漫久久精久,九九在线精品视频播放,黄色成人免费观看,三级成人影院,久碰久,四虎成人欧美精品在永久在线

掃一掃
關(guān)注微信公眾號(hào)

Linux線程比較:LinuxThreads 和NPTL
2007-02-05   中國(guó)IT實(shí)驗(yàn)室

LinuxThreads 項(xiàng)目最初將多線程的概念引入了 Linux?,但是 LinuxThreads 并不遵守 POSIX 線程標(biāo)準(zhǔn)。盡管更新的 Native POSIX Thread Library(NPTL)庫(kù)填補(bǔ)了一些空白,但是這仍然存在一些問題。本文為那些需要將自己的應(yīng)用程序從 LinuxThreads 移植到 NPTL 上或者只是希望理解有何區(qū)別的開發(fā)人員介紹這兩種 Linux 線程模型之間的區(qū)別。

當(dāng) Linux 最初開發(fā)時(shí),在內(nèi)核中并不能真正支持線程。但是它的確可以通過(guò) clone() 系統(tǒng)調(diào)用將進(jìn)程作為可調(diào)度的實(shí)體。這個(gè)調(diào)用創(chuàng)建了調(diào)用進(jìn)程(calling process)的一個(gè)拷貝,這個(gè)拷貝與調(diào)用進(jìn)程共享相同的地址空間。LinuxThreads 項(xiàng)目使用這個(gè)調(diào)用來(lái)完全在用戶空間模擬對(duì)線程的支持。不幸的是,這種方法有一些缺點(diǎn),尤其是在信號(hào)處理、調(diào)度和進(jìn)程間同步原語(yǔ)方面都存在問題。另外,這個(gè)線程模型也不符合 POSIX 的要求。

要改進(jìn) LinuxThreads,非常明顯我們需要內(nèi)核的支持,并且需要重寫線程庫(kù)。有兩個(gè)相互競(jìng)爭(zhēng)的項(xiàng)目開始來(lái)滿足這些要求。一個(gè)包括 IBM 的開發(fā)人員的團(tuán)隊(duì)開展了 NGPT(Next-Generation POSIX Threads)項(xiàng)目。同時(shí),Red Hat 的一些開發(fā)人員開展了 NPTL 項(xiàng)目。NGPT 在 2003 年中期被放棄了,把這個(gè)領(lǐng)域完全留給了 NPTL。

盡管從 LinuxThreads 到 NPTL 看起來(lái)似乎是一個(gè)必然的過(guò)程,但是如果您正在為一個(gè)歷史悠久的 Linux 發(fā)行版維護(hù)一些應(yīng)用程序,并且計(jì)劃很快就要進(jìn)行升級(jí),那么如何遷移到 NPTL 上就會(huì)變成整個(gè)移植過(guò)程中重要的一個(gè)部分。另外,我們可能會(huì)希望了解二者之間的區(qū)別,這樣就可以對(duì)自己的應(yīng)用程序進(jìn)行設(shè)計(jì),使其能夠更好地利用這兩種技術(shù)。

本文詳細(xì)介紹了這些線程模型分別是在哪些發(fā)行版上實(shí)現(xiàn)的。

LinuxThreads 設(shè)計(jì)細(xì)節(jié)

線程 將應(yīng)用程序劃分成一個(gè)或多個(gè)同時(shí)運(yùn)行的任務(wù)。線程與傳統(tǒng)的多任務(wù)進(jìn)程 之間的區(qū)別在于:線程共享的是單個(gè)進(jìn)程的狀態(tài)信息,并會(huì)直接共享內(nèi)存和其他資源。同一個(gè)進(jìn)程中線程之間的上下文切換通常要比進(jìn)程之間的上下文切換速度更快。因此,多線程程序的優(yōu)點(diǎn)就是它可以比多進(jìn)程應(yīng)用程序的執(zhí)行速度更快。另外,使用線程我們可以實(shí)現(xiàn)并行處理。這些相對(duì)于基于進(jìn)程的方法所具有的優(yōu)點(diǎn)推動(dòng)了 LinuxThreads 的實(shí)現(xiàn)。

LinuxThreads 最初的設(shè)計(jì)相信相關(guān)進(jìn)程之間的上下文切換速度很快,因此每個(gè)內(nèi)核線程足以處理很多相關(guān)的用戶級(jí)線程。這就導(dǎo)致了一對(duì)一 線程模型的革命。

讓我們來(lái)回顧一下 LinuxThreads 設(shè)計(jì)細(xì)節(jié)的一些基本理念:

  • LinuxThreads 非常出名的一個(gè)特性就是管理線程(manager thread)。管理線程可以滿足以下要求:

    • 系統(tǒng)必須能夠響應(yīng)終止信號(hào)并殺死整個(gè)進(jìn)程。
    • 以堆棧形式使用的內(nèi)存回收必須在線程完成之后進(jìn)行。因此,線程無(wú)法自行完成這個(gè)過(guò)程。
    • 終止線程必須進(jìn)行等待,這樣它們才不會(huì)進(jìn)入僵尸狀態(tài)。
    • 線程本地?cái)?shù)據(jù)的回收需要對(duì)所有線程進(jìn)行遍歷;這必須由管理線程來(lái)進(jìn)行。
    • 如果主線程需要調(diào)用 pthread_exit(),那么這個(gè)線程就無(wú)法結(jié)束。主線程要進(jìn)入睡眠狀態(tài),而管理線程的工作就是在所有線程都被殺死之后來(lái)喚醒這個(gè)主線程。

  • 為了維護(hù)線程本地?cái)?shù)據(jù)和內(nèi)存,LinuxThreads 使用了進(jìn)程地址空間的高位內(nèi)存(就在堆棧地址之下)。

  • 原語(yǔ)的同步是使用信號(hào) 來(lái)實(shí)現(xiàn)的。例如,線程會(huì)一直阻塞,直到被信號(hào)喚醒為止。

  • 在克隆系統(tǒng)的最初設(shè)計(jì)之下,LinuxThreads 將每個(gè)線程都是作為一個(gè)具有惟一進(jìn)程 ID 的進(jìn)程實(shí)現(xiàn)的。

  • 終止信號(hào)可以殺死所有的線程。LinuxThreads 接收到終止信號(hào)之后,管理線程就會(huì)使用相同的信號(hào)殺死所有其他線程(進(jìn)程)。

  • 根據(jù) LinuxThreads 的設(shè)計(jì),如果一個(gè)異步信號(hào)被發(fā)送了,那么管理線程就會(huì)將這個(gè)信號(hào)發(fā)送給一個(gè)線程。如果這個(gè)線程現(xiàn)在阻塞了這個(gè)信號(hào),那么這個(gè)信號(hào)也就會(huì)被掛起。這是因?yàn)楣芾砭€程無(wú)法將這個(gè)信號(hào)發(fā)送給進(jìn)程;相反,每個(gè)線程都是作為一個(gè)進(jìn)程在執(zhí)行。

  • 線程之間的調(diào)度是由內(nèi)核調(diào)度器來(lái)處理的。

LinuxThreads 及其局限性

LinuxThreads 的設(shè)計(jì)通常都可以很好地工作;但是在壓力很大的應(yīng)用程序中,它的性能、可伸縮性和可用性都會(huì)存在問題。下面讓我們來(lái)看一下 LinuxThreads 設(shè)計(jì)的一些局限性:

  • 它使用管理線程來(lái)創(chuàng)建線程,并對(duì)每個(gè)進(jìn)程所擁有的所有線程進(jìn)行協(xié)調(diào)。這增加了創(chuàng)建和銷毀線程所需要的開銷。

  • 由于它是圍繞一個(gè)管理線程來(lái)設(shè)計(jì)的,因此會(huì)導(dǎo)致很多的上下文切換的開銷,這可能會(huì)妨礙系統(tǒng)的可伸縮性和性能。

  • 由于管理線程只能在一個(gè) CPU 上運(yùn)行,因此所執(zhí)行的同步操作在 SMP 或 NUMA 系統(tǒng)上可能會(huì)產(chǎn)生可伸縮性的問題。

  • 由于線程的管理方式,以及每個(gè)線程都使用了一個(gè)不同的進(jìn)程 ID,因此 LinuxThreads 與其他與 POSIX 相關(guān)的線程庫(kù)并不兼容。

  • 信號(hào)用來(lái)實(shí)現(xiàn)同步原語(yǔ),這會(huì)影響操作的響應(yīng)時(shí)間。另外,將信號(hào)發(fā)送到主進(jìn)程的概念也并不存在。因此,這并不遵守 POSIX 中處理信號(hào)的方法。

  • LinuxThreads 中對(duì)信號(hào)的處理是按照每線程的原則建立的,而不是按照每進(jìn)程的原則建立的,這是因?yàn)槊總€(gè)線程都有一個(gè)獨(dú)立的進(jìn)程 ID。由于信號(hào)被發(fā)送給了一個(gè)專用的線程,因此信號(hào)是串行化的 —— 也就是說(shuō),信號(hào)是透過(guò)這個(gè)線程再傳遞給其他線程的。這與 POSIX 標(biāo)準(zhǔn)對(duì)線程進(jìn)行并行處理的要求形成了鮮明的對(duì)比。例如,在 LinuxThreads 中,通過(guò) kill() 所發(fā)送的信號(hào)被傳遞到一些單獨(dú)的線程,而不是集中整體進(jìn)行處理。這意味著如果有線程阻塞了這個(gè)信號(hào),那么 LinuxThreads 就只能對(duì)這個(gè)線程進(jìn)行排隊(duì),并在線程開放這個(gè)信號(hào)時(shí)在執(zhí)行處理,而不是像其他沒有阻塞信號(hào)的線程中一樣立即處理這個(gè)信號(hào)。

  • 由于 LinuxThreads 中的每個(gè)線程都是一個(gè)進(jìn)程,因此用戶和組 ID 的信息可能對(duì)單個(gè)進(jìn)程中的所有線程來(lái)說(shuō)都不是通用的。例如,一個(gè)多線程的 setuid()/setgid() 進(jìn)程對(duì)于不同的線程來(lái)說(shuō)可能都是不同的。

  • 有一些情況下,所創(chuàng)建的多線程核心轉(zhuǎn)儲(chǔ)中并沒有包含所有的線程信息。同樣,這種行為也是每個(gè)線程都是一個(gè)進(jìn)程這個(gè)事實(shí)所導(dǎo)致的結(jié)果。如果任何線程發(fā)生了問題,我們?cè)谙到y(tǒng)的核心文件中只能看到這個(gè)線程的信息。不過(guò),這種行為主要適用于早期版本的 LinuxThreads 實(shí)現(xiàn)。

  • 由于每個(gè)線程都是一個(gè)單獨(dú)的進(jìn)程,因此 /proc 目錄中會(huì)充滿眾多的進(jìn)程項(xiàng),而這實(shí)際上應(yīng)該是線程。

  • 由于每個(gè)線程都是一個(gè)進(jìn)程,因此對(duì)每個(gè)應(yīng)用程序只能創(chuàng)建有限數(shù)目的線程。例如,在 IA32 系統(tǒng)上,可用進(jìn)程總數(shù) —— 也就是可以創(chuàng)建的線程總數(shù) —— 是 4,090。

  • 由于計(jì)算線程本地?cái)?shù)據(jù)的方法是基于堆棧地址的位置的,因此對(duì)于這些數(shù)據(jù)的訪問速度都很慢。另外一個(gè)缺點(diǎn)是用戶無(wú)法可信地指定堆棧的大小,因?yàn)橛脩艨赡軙?huì)意外地將堆棧地址映射到本來(lái)要為其他目的所使用的區(qū)域上了。按需增長(zhǎng)(grow on demand) 的概念(也稱為浮動(dòng)堆棧 的概念)是在 2.4.10 版本的 Linux 內(nèi)核中實(shí)現(xiàn)的。在此之前,LinuxThreads 使用的是固定堆棧。

關(guān)于 NPTL

NPTL,或稱為 Native POSIX Thread Library,是 Linux 線程的一個(gè)新實(shí)現(xiàn),它克服了 LinuxThreads 的缺點(diǎn),同時(shí)也符合 POSIX 的需求。與 LinuxThreads 相比,它在性能和穩(wěn)定性方面都提供了重大的改進(jìn)。與 LinuxThreads 一樣,NPTL 也實(shí)現(xiàn)了一對(duì)一的模型。

Ulrich Drepper 和 Ingo Molnar 是 Red Hat 參與 NPTL 設(shè)計(jì)的兩名員工。他們的總體設(shè)計(jì)目標(biāo)如下:

  • 這個(gè)新線程庫(kù)應(yīng)該兼容 POSIX 標(biāo)準(zhǔn)。

  • 這個(gè)線程實(shí)現(xiàn)應(yīng)該在具有很多處理器的系統(tǒng)上也能很好地工作。

  • 為一小段任務(wù)創(chuàng)建新線程應(yīng)該具有很低的啟動(dòng)成本。

  • NPTL 線程庫(kù)應(yīng)該與 LinuxThreads 是二進(jìn)制兼容的。注意,為此我們可以使用 LD_ASSUME_KERNEL,這會(huì)在本文稍后進(jìn)行討論。

  • 這個(gè)新線程庫(kù)應(yīng)該可以利用 NUMA 支持的優(yōu)點(diǎn)。

NPTL 的優(yōu)點(diǎn)

與 LinuxThreads 相比,NPTL 具有很多優(yōu)點(diǎn):

  • NPTL 沒有使用管理線程。管理線程的一些需求,例如向作為進(jìn)程一部分的所有線程發(fā)送終止信號(hào),是并不需要的;因?yàn)閮?nèi)核本身就可以實(shí)現(xiàn)這些功能。內(nèi)核還會(huì)處理每個(gè)線程堆棧所使用的內(nèi)存的回收工作。它甚至還通過(guò)在清除父線程之前進(jìn)行等待,從而實(shí)現(xiàn)對(duì)所有線程結(jié)束的管理,這樣可以避免僵尸進(jìn)程的問題。

  • 由于 NPTL 沒有使用管理線程,因此其線程模型在 NUMA 和 SMP 系統(tǒng)上具有更好的可伸縮性和同步機(jī)制。

  • 使用 NPTL 線程庫(kù)與新內(nèi)核實(shí)現(xiàn),就可以避免使用信號(hào)來(lái)對(duì)線程進(jìn)行同步了。為了這個(gè)目的,NPTL 引入了一種名為 futex 的新機(jī)制。futex 在共享內(nèi)存區(qū)域上進(jìn)行工作,因此可以在進(jìn)程之間進(jìn)行共享,這樣就可以提供進(jìn)程間 POSIX 同步機(jī)制。我們也可以在進(jìn)程之間共享一個(gè) futex。這種行為使得進(jìn)程間同步成為可能。實(shí)際上,NPTL 包含了一個(gè) PTHREAD_PROCESS_SHARED 宏,使得開發(fā)人員可以讓用戶級(jí)進(jìn)程在不同進(jìn)程的線程之間共享互斥鎖。

  • 由于 NPTL 是 POSIX 兼容的,因此它對(duì)信號(hào)的處理是按照每進(jìn)程的原則進(jìn)行的;getpid() 會(huì)為所有的線程返回相同的進(jìn)程 ID。例如,如果發(fā)送了 SIGSTOP 信號(hào),那么整個(gè)進(jìn)程都會(huì)停止;使用 LinuxThreads,只有接收到這個(gè)信號(hào)的線程才會(huì)停止。這樣可以在基于 NPTL 的應(yīng)用程序上更好地利用調(diào)試器,例如 GDB。

  • 由于在 NPTL 中所有線程都具有一個(gè)父進(jìn)程,因此對(duì)父進(jìn)程匯報(bào)的資源使用情況(例如 CPU 和內(nèi)存百分比)都是對(duì)整個(gè)進(jìn)程進(jìn)行統(tǒng)計(jì)的,而不是對(duì)一個(gè)線程進(jìn)行統(tǒng)計(jì)的。

  • NPTL 線程庫(kù)所引入的一個(gè)實(shí)現(xiàn)特性是對(duì) ABI(應(yīng)用程序二進(jìn)制接口)的支持。這幫助實(shí)現(xiàn)了與 LinuxThreads 的向后兼容性。這個(gè)特性是通過(guò)使用 LD_ASSUME_KERNEL 實(shí)現(xiàn)的,下面就來(lái)介紹這個(gè)特性。

LD_ASSUME_KERNEL 環(huán)境變量

正如上面介紹的一樣,ABI 的引入使得可以同時(shí)支持 NPTL 和 LinuxThreads 模型?;旧蟻?lái)說(shuō),這是通過(guò) ld (一個(gè)動(dòng)態(tài)鏈接器/加載器)來(lái)進(jìn)行處理的,它會(huì)決定動(dòng)態(tài)鏈接到哪個(gè)運(yùn)行時(shí)線程庫(kù)上。

舉例來(lái)說(shuō),下面是 WebSphere® Application Server 對(duì)這個(gè)變量所使用的一些通用設(shè)置;您可以根據(jù)自己的需要進(jìn)行適當(dāng)?shù)脑O(shè)置:

  • LD_ASSUME_KERNEL=2.4.19:這會(huì)覆蓋 NPTL 的實(shí)現(xiàn)。這種實(shí)現(xiàn)通常都表示使用標(biāo)準(zhǔn)的 LinuxThreads 模型,并啟用浮動(dòng)堆棧的特性。
  • LD_ASSUME_KERNEL=2.2.5:這會(huì)覆蓋 NPTL 的實(shí)現(xiàn)。這種實(shí)現(xiàn)通常都表示使用 LinuxThreads 模型,同時(shí)使用固定堆棧大小。

我們可以使用下面的命令來(lái)設(shè)置這個(gè)變量:

export LD_ASSUME_KERNEL=2.4.19

注意,對(duì)于任何 LD_ASSUME_KERNEL 設(shè)置的支持都取決于目前所支持的線程庫(kù)的 ABI 版本。例如,如果線程庫(kù)并不支持 2.2.5 版本的 ABI,那么用戶就不能將 LD_ASSUME_KERNEL 設(shè)置為 2.2.5。通常,NPTL 需要 2.4.20,而 LinuxThreads 則需要 2.4.1。

如果您正運(yùn)行的是一個(gè)啟用了 NPTL 的 Linux 發(fā)行版,但是應(yīng)用程序卻是基于 LinuxThreads 模型來(lái)設(shè)計(jì)的,那么所有這些設(shè)置通常都可以使用。

GNU_LIBPTHREAD_VERSION 宏

大部分現(xiàn)代 Linux 發(fā)行版都預(yù)裝了 LinuxThreads 和 NPTL,因此它們提供了一種機(jī)制來(lái)在二者之間進(jìn)行切換。要查看您的系統(tǒng)上正在使用的是哪個(gè)線程庫(kù),請(qǐng)運(yùn)行下面的命令:

$ getconf GNU_LIBPTHREAD_VERSION

這會(huì)產(chǎn)生類似于下面的輸出結(jié)果:

NPTL 0.34

或者:

linuxthreads-0.10

Linux 發(fā)行版所使用的線程模型、glibc 版本和內(nèi)核版本

表 1 列出了一些流行的 Linux 發(fā)行版,以及它們所采用的線程實(shí)現(xiàn)的類型、glibc 庫(kù)和內(nèi)核版本。

表 1. Linux 發(fā)行版及其線程實(shí)現(xiàn)
線程實(shí)現(xiàn) C 庫(kù) 發(fā)行版 內(nèi)核
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 版本的內(nèi)核和 glibc 2.3.3 開始,NPTL 所采用的版本號(hào)命名約定發(fā)生了變化:這個(gè)庫(kù)現(xiàn)在是根據(jù)所使用的 glibc 的版本進(jìn)行編號(hào)的。

Java™ 虛擬機(jī)(JVM)的支持可能會(huì)稍有不同。IBM 的 JVM 可以支持表 1 中 glibc 版本高于 2.1 的大部分發(fā)行版。

結(jié)束語(yǔ)

LinuxThreads 的限制已經(jīng)在 NPTL 以及 LinuxThreads 后期的一些版本中得到了克服。例如,最新的 LinuxThreads 實(shí)現(xiàn)使用了線程注冊(cè)來(lái)定位線程本地?cái)?shù)據(jù);例如在 Intel® 處理器上,它就使用了 %fs 和 %gs 段寄存器來(lái)定位訪問線程本地?cái)?shù)據(jù)所使用的虛擬地址。盡管這個(gè)結(jié)果展示了 LinuxThreads 所采納的一些修改的改進(jìn)結(jié)果,但是它在更高負(fù)載和壓力測(cè)試中,依然存在很多問題,因?yàn)樗^(guò)分地依賴于一個(gè)管理線程,使用它來(lái)進(jìn)行信號(hào)處理等操作。

您應(yīng)該記住,在使用 LinuxThreads 構(gòu)建庫(kù)時(shí),需要使用 -D_REENTRANT 編譯時(shí)標(biāo)志。這使得庫(kù)線程是安全的。

最后,也許是最重要的事情,請(qǐng)記住 LinuxThreads 項(xiàng)目的創(chuàng)建者已經(jīng)不再積極更新它了,他們認(rèn)為 NPTL 會(huì)取代 LinuxThreads。

LinuxThreads 的缺點(diǎn)并不意味著 NPTL 就沒有錯(cuò)誤。作為一個(gè)面向 SMP 的設(shè)計(jì),NPTL 也有一些缺點(diǎn)。我曾經(jīng)看到過(guò)在最近的 Red Hat 內(nèi)核上出現(xiàn)過(guò)這樣的問題:一個(gè)簡(jiǎn)單線程在單處理器的機(jī)器上運(yùn)行良好,但在 SMP 機(jī)器上卻掛起了。我相信在 Linux 上還有更多工作要做才能使它具有更好的可伸縮性,從而滿足高端應(yīng)用程序的需求。

熱詞搜索:

上一篇:集成VoIP和CRM功能的開源Tirxbox (下)
下一篇:數(shù)據(jù)中心管理邁向標(biāo)準(zhǔn)化

分享到: 收藏