The C10K problem原文翻译
原文地址:http://www.cnblogs.com/fll/archive/2008/05/17/1201540.html
?
The C10K problem
?
如今的web服務器需要同時處理一萬個以上的客戶端了,難道不是嗎?畢竟如今的網絡是個big place了。
現在的計算機也很強大了,你只需要花大概$1200就可以買一個1000MHz的處理器,2G的內存, 1000Mbit/sec的網卡的機器。讓我們來看看–20000個客戶,每個為50KHz,100Kbyes和 50Kbit/sec,那么沒有什么比為這兩萬個客戶端的每個每秒從硬盤讀取4千字節然后發送到網絡上 去更消耗資源的了。可以看出硬件不再是瓶頸了。 (That works out to $0.08 per client, by the way. Those $100/client licensing fees some operating systems charge are starting to look a little heavy!)
在1999年最繁忙的ftp站點,cdrom.com,盡管有G比特的網絡帶寬,卻也只能同時處理10000個 客戶端。在2001年,同樣的速度可以被幾個ISP服務商所提供,他們預期該趨勢會因為大量的商業 用戶而變得越來越普遍。
目前的瘦客戶端模型也開始又變得流行起來了–服務器運行在Internet上,為數千個客戶端服務。
基于以上一些考慮,這里就配置操作系統或者編寫支持數千個網絡客戶端的代碼問題提出一些 注意點,該論題是基于類Unix操作系統的–該系統是我的個人愛好,當然Windows也有占有一席之地。
Contents
The C10K problem
相關網站
須首先閱讀的書籍
I/O框架
I/O策略 
Serve many clients with each thread, and use nonblocking I/O and level-triggered readiness notification 
The traditional select()
The traditional poll()
/dev/poll (Solaris 2.7+)
kqueue (FreeBSD, NetBSD)
Serve many clients with each thread, and use nonblocking I/O and readiness change notification 
epoll (Linux 2.6+)
Polyakov’s kevent (Linux 2.6+)
Drepper’s New Network Interface (proposal for Linux 2.6+)
Realtime Signals (Linux 2.4+)
Signal-per-fd
kqueue (FreeBSD, NetBSD)
Serve many clients with each thread, and use asynchronous I/O and completion notification
Serve one client with each server thread 
LinuxThreads (Linux 2.0+)
NGPT (Linux 2.4+)
NPTL (Linux 2.6, Red Hat 9)
FreeBSD threading support
NetBSD threading support
Solaris threading support
Java threading support in JDK 1.3.x and earlier
Note: 1:1 threading vs. M:N threading
Build the server code into the kernel
Comments
Limits on open filehandles
Limits on threads
Java issues [Updated 27 May 2001]
Other tips 
Zero-Copy
The sendfile() system call can implement zero-copy networking.
Avoid small frames by using writev (or TCP_CORK)
Some programs can benefit from using non-Posix threads.
Caching your own data can sometimes be a win.
Other limits
Kernel Issues
Measuring Server Performance
Examples 
Interesting select()-based servers
Interesting /dev/poll-based servers
Interesting kqueue()-based servers
Interesting realtime signal-based servers
Interesting thread-based servers
Interesting in-kernel servers
Other interesting links
?
Related Sites
2003年10月,Felix von Leitner整理了一個很好的網站和一個 presentation,該網站介紹了網絡的可測量性,完成 了以不同網絡系統調用和不同的操作系統為基準的性能比較。其中一項就是2.6版本的Linux內核 擊敗了2.4的內核,當然還有許多的圖片可以給OS的開發者在平時提供點想法。
(See also the Slashdot comments; it’ll be interesting to see whether anyone does followup benchmarks improving on Felix’s results.)
Book to Read First
如果你還沒有讀過W.Richard Stevens先生的《Unix網絡編程:第一卷》的話,請盡快獲取一份 拷貝,該書描述了許多關于編寫高性能的服務器的I/O策略和各自的一些缺陷,甚至還講述 了“thundering herd”問題,同時你也可以閱讀 Jeff Darcy寫的關于高性能服務器設計的一些 notes。
(Another book which might be more helpful for those who are *using* rather than *writing* a web server is Building Scalable Web Sites by Cal Henderson.)
I/O框架
以下所列的為幾個包裝好的庫,它們概要了幾中常見的技巧,并且可以使你的代碼與具體操作 系統隔離,從而具有更好的移植性。
ACE, 一個重量級的C++ I/O框架,用面向對象實現了一些I/O策略和其它有用的東西,特別的, 它的Reactor是用OO方式處理非阻塞I/O,而Proactor是用OO方式處理異步I/O的( In particular, his Reactor is an OO way of doing nonblocking I/O, and Proactor is an OO way of doing asynchronous I/O).
ASIO 一個C++的I/O框架,逐漸成為Boost庫的一部分。it’s like ACE updated for the STL era。
libevent 由Niels Provos用C編寫的一個輕量級的I/O框架。它支持kqueue和select,并且很 快就可以支持poll和epoll(翻譯此文時已經支持)。我想它應該是只采用了水平觸發機制,該機制 有好處當然也有不好的地方。Niels給出了一張圖 來說明時間和連接數目在處理一個事件上的功能,從圖上可以看出kqueue和sys_epoll明顯勝出。
我本人也嘗試過輕量級的框架(很可惜沒有維持至今): 
Poller 是一個輕量級的C++ I/O框架,它使用任何一種準備就緒API(poll, select, /dev/poll, kqueue, sigio)實現水平觸發準備就緒API。以其它不同的API為基準 ,Poller的性能 好得多。該鏈接文檔的下面一部分說明了如何使用這些準備就緒API。
rn 是一個輕量級的C I/O框架,也是我繼Poller后的第二個框架。該框架可以很容易的被用 于商業應用中,也容易的適用于非C++應用中。它如今已經在幾個商業產品中使用。
Matt Welsh在2000年四月關于在構建服務器方面如何平衡工作線程和事件驅動技術的使用寫了 一篇論文,在該論文中描述了他自己的Sandstorm I/O框架。
Cory Nelson’s Scale! library - 一個Windows下的異步套接字,文件和管道的庫。
I/O Strategies
網絡軟件設計者往往有很多種選擇,以下列出一些:
是否處理多個I/O?如何處理在單一線程中的多個I/O調用? 
不處理,從頭到尾使用阻塞和同步I/O調用,可以使用多線程或多進程來達到并發效果。
使用非阻塞調用(如在一個設置O_NONBLOCK選項的socket上使用write)讀取I/O,當I/O完 成時發出通知(如poll,/dev/poll)從而開始下一個I/O。這種主要使用在網絡I/O上,而不是磁盤的I/O上。
使用異步調用(如aio_write())讀取I/O,當I/O完成時會發出通知(如信號或者完成端口),可以同時使用在網絡I/O和磁盤I/O上。
如何控制對每個客戶的服務? 
對每個客戶使用一個進程(經典的Unix方法,自從1980年一直使用)
一個系統級的線程處理多個客戶,每個客戶是如下一種: 
a user-level thread (e.g. GNU state threads, classic Java with green threads)
a state machine (a bit esoteric, but popular in some circles; my favorite)
a continuation (a bit esoteric, but popular in some circles)
o一個系統級的線程對應一個客戶端(e.g. classic Java with native threads)
一個系統級的線程對應每一個活動的客戶端(e.g. Tomcat with apache front end; NT完成端口; 線程池)
是否使用標準的操作系統服務,還是把一些代碼放入內核中(如自定義驅動,內核模塊,VxD)。
下面的五種方式應該是最常用的了。
一個線程服務多個客戶端,使用非阻塞I/O和水平觸發的就緒通知
一個線程服務多個客戶端,使用非阻塞I/O和就緒改變時通知
一個服務線程服務多個客戶端,使用異步I/O
一個服務線程服務一個客戶端,使用阻塞I/O
把服務代碼編譯進內核
1. 一個線程服務多個客戶端,使用非阻塞I/O和水平觸發的就緒通知
…把網絡句柄設置為非阻塞模型,然后使用select()或poll()來告知哪個句柄已有數據在等待 處理。此模型是最傳統的,在此模型下,由內核告知你某個文件描述符是否準備好,是否已經完 成你的任務自從上次內核告知已準備好以來(“水平觸發”這個名字來源計算機硬件設計,與其 相對的是“邊緣觸發”,Jonathon Lemon在它的關于kqueue() 的論文中介紹了這兩個術語)。
注意:牢記內核的就緒通知僅僅只是個提示,當你試圖從一個文件描述符讀取數據時,該文件 描述符可能并沒有準備好。這就是為什么需要在使用就緒通知的時候使用非阻塞模型的原因。
一個重要的瓶頸是read()或sendfile()從磁盤塊讀取時,如果該頁當前并不在內存中。設置磁 盤文件描述符為非阻塞沒有任何影響。同樣的問題也發生在內存映射磁盤文件中。首先一個服務 需要磁盤I/O時,進程塊和所有的客戶端都必須等待,因此最初的非線程的性能就被消耗了。
這也是異步I/O的目的,當然僅限于沒有AIO的系統。處理磁盤I/O的工作線程或工作進程也可能遭遇此 瓶頸。一條途徑就是使用內存映射文件,如果mincore()指明I/O必需的話,那么要求一個工作線 程來完成此I/O,然后繼續處理網絡事件。Jef Poskanzer提到Pai,Druschel和Zwaenepoel的 Flash web服務器使用了這個方法,并且他們就此在 Usenix’99上做了一個演講,看上去就好像 FreeBSD和Solaris 中提供了mincore()一樣,但是它并不是Single Unix Specification的一部分,在Linux的2.3.51 的內核中提供了該方法,感謝Chuck Lever。
在2003.11的 freebsd-hackers list中,Vivek Pei上報了一個不錯的成果,他們利用系統剖析 工具剖析它們的Flash Web服務器,然后再攻擊其瓶頸。其中找到的一個瓶頸就是mincore(猜測 畢竟不是好辦法),另外一個就是sendfile在磁盤塊訪問時。他們修改了sendfile(),當需要讀 取的頁不在內存中時則返回類似EWOULDBLOCK的值,從而提高了性能。The end result of their optimizations is a SpecWeb99 score of about 800 on a 1GHZ/1GB FreeBSD box, which is better than anything on file at spec.org.
在非阻塞套接字的集合中,關于單一線程是如何告知哪個套接字是準備就緒的,以下列出了幾 種方法:
傳統的select()
遺憾的是,select()受限于FD_SETSIZE個句柄。該限制被編譯進了標準庫和用戶程序(有些 版本的C library允許你在用戶程序編譯時放寬該限制)。See Poller_select (cc, h) for an example of how to use select() interchangeably with other readiness notification schemes.
?
傳統的poll()
poll()雖然沒有文件描述符個數的硬編碼限制,但是當有數千個時速度就會變得很慢,因為 大多數的文件描述符在某個時間是空閑的,徹底掃描數千個描述符是需要花費一定時間的。有些操作系統(如Solaris 8)通過使用了poll hinting技術改進了poll(),該技術由Niels Provos在1999年實現并利用基準測試程序測試過。
See Poller_poll (cc, h, benchmarks) for an example of how to use poll() interchangeably with other readiness notification schemes.
?
/dev/poll
這是在Solaris中被推薦的代替poll的方法。/dev/poll的背后思想就是利用poll()在大部分的調用時使用相同的參數。使用/dev/poll時 ,首先打開/dev/poll得到文件描述符,然后把你關心的文件描述符寫入到/dev/poll的描述符, 然后你就可以從/dev/poll的描述符中讀取到已就緒的文件描述符。
/dev/poll 在Solaris 7(see patchid 106541) 中就已經存在,不過在Solaris 8 中才公開現身。在750個客戶端的情況下,this has 10% of the overhead of poll()。
關于/dev/poll在Linux上有多種不同的嘗試實現,但是沒有一種可以和epoll相比,不推薦在 Linux上使用/dev/poll。
See Poller_devpoll (cc, h benchmarks ) for an example of how to use /dev/poll interchangeably with many other readiness notification schemes. (Caution - the example is for Linux /dev/poll, might not work right on Solaris.)
?
kqueue()
這是在FreeBSD系統上推薦使用的代替poll的方法(and, soon, NetBSD).kqueue()即可以水平觸發,也可以邊緣觸發,具體請看下面.
2. 一個線程服務多個客戶端,使用非阻塞I/O和就緒改變時通知
Readiness change notification(或邊緣觸發就緒通知)的意思就是當你給內核一個文件描述 符,一段時間后,如果該文件描述符從沒有就緒到已經準備就緒,那么內核就會發出通知,告知 該文件描述符已經就緒,并且不會再對該描述符發出類似的就緒通知直到你在描述符上進行一些 操作使得該描述符不再就緒(如直到在send,recv或者accept等調用上遇到EWOULDBLOCK錯誤,或 者發送/接收了少于需要的字節數)。
當使用Readiness change notification時,必須準備好處理亂真事件,因為最常見的實現是只 要接收到任何數據包都發出就緒信號,而不管文件描述符是否準備就緒。
這是水平觸發的就緒通知的相對應的機制。It’s a bit less forgiving of programming mistakes, since if you miss just one event, the connection that event was for gets stuck forever. 然而,我發現edge-triggered readiness notification可以使編寫帶OpenSSL的 非阻塞客戶端更簡單,可以試下。
[Banga, Mogul, Drusha '99]詳細描述了這種模型.
有幾種APIs可以使得應用程序獲得“文件描述符已就緒”的通知:
kqueue() 這是在FreeBSD系統上推薦使用邊緣觸發的方法 (and, soon, NetBSD).FreeBSD 4.3及以后版本,NetBSD(2002.10)都支持 kqueue()/kevent(), 支持邊沿觸發和水平觸發(請查看Jonathan Lemon 的網頁和他的BSDCon 2000關于kqueue的論文)。 
就像/dev/poll一樣,你分配一個監聽對象,不過不是打開文件/dev/poll,而是調用kqueue ()來獲得。需要改變你所監聽的事件或者獲得當前事件的列表,可以在kqueue()返回的描述符上 調用kevent()來達到目的。它不僅可以監聽套接字,還可以監聽普通的文件的就緒,信號和I/O完 成的事件也可以.
Note: 在2000.10,FreeBSD的線程庫和kqueue()并不能一起工作得很好,當kqueue()阻塞時, 那么整個進程都將會阻塞,而不僅僅是調用kqueue()的線程。
See Poller_kqueue (cc, h, benchmarks) for an example of how to use kqueue() interchangeably with many other readiness notification schemes.
使用kqueue()的例程和庫:
PyKQueue — 一個Python的kqueue()庫.
Ronald F.Guilmette的echo的服務器例程; 另外可以查看他在 2000.9.28在freebsd 上發表的帖子。
?
epoll
這是Linux 2.6的內核中推薦使用的邊沿觸發poll.2001.7.11, Davide Libenzi提議了一個實時信號的可選方法,他稱之為/dev/epoll< /a>, 該方法類似與實時信號就緒通知機制,但是結合了其它更多的事件,從而在大多數的事件獲取上擁有更高的效率。 
epoll在將它的接口從一個/dev下的指定文件改變為系統調用sys_epoll后就合并到2.5版本的 Linux內核開發樹中,另外也提供了一個為2.4老版本的內核可以使用epoll的補丁。 
unifying epoll, aio, 2002 年萬圣節前夕的Linux內核郵件列表就統一epoll,aio和其它的event sources 展開了很久的爭論,it may yet happen,but Davide is concentrating on firming up epoll in general first.
?
Polyakov’s kevent (Linux 2.6+) 的最后新聞:2006.2.9和2006.7.9,Evgeniy Polyakov發表了融合epoll和 aio的補丁,他的目標是支持網絡AIO. See: 
the LWN article about kevent
his July announcement
his kevent page
his naio page
some recent discussion
?
Drepper的最新網絡接口 (proposal for Linux 2.6+)
在2006 OLS上,Ulrich Drepper提議了一種最新的高速異步網絡API. See:
his paper, “The Need for Asynchronous, Zero-Copy Network I/O“
his slides
LWN article from July 22
?
Realtime Signals實時信號
Linux2.4內核中推薦使用的邊沿觸發poll.2.4的linux內核可以通過實時信號來分派套接字事件,示例如下:
/* Mask off SIGIO and the signal you want to use. */ sigemptyset(&sigset); sigaddset(&sigset, signum); sigaddset(&sigset, SIGIO); sigprocmask(SIG_BLOCK, &m_sigset, NULL); /* For each file descriptor, invoke F_SETOWN, F_SETSIG, and set O_ASYNC. */ fcntl(fd, F_SETOWN, (int) getpid()); fcntl(fd, F_SETSIG, signum); flags = fcntl(fd, F_GETFL); flags |= O_NONBLOCK|O_ASYNC; fcntl(fd, F_SETFL, flags);
當正常的I/O函數如read()或write()完成時,發送信號。要使用該段的話,在外層循環中編寫 一個普通的poll(),在循環里面,當poll()處理完所有的描述符后,進入 sigwaitinfo()循環。 如果sigwaitinfo()或sigtimedwait()返回了實時信號,那么siginfo.si_fd和 siginfo_si_band給出的信息和調用poll()后pollfd.fd和pollfd.revents的幾乎一樣。如果你處 理該I/O,那么就繼續調用sigwaitinfo()。
如果sigwaitinfo()返回了傳統的SIGIO,那么信號隊列溢出了,你必須通過臨時 改變信號處理 程序為SIG_DFL來刷新信號隊列,然后返回到外層的poll()循環。
See Poller_sigio (cc, h) for an example of how to use rtsignals interchangeably with many other readiness notification schemes.
See Zach Brown’s phhttpd 示例代碼來如何直接使用這些特點. (Or don’t; phhttpd is a bit hard to figure out…)
[Provos, Lever, and Tweedie 2000] 描述了最新的phhttp的基準測試,使用了不同的sigtimewait()和sigtimedwait4(),這些調用可以使你只用一次調用便獲得多個信號。 有趣的是,sigtimedwait4()的主要好處是它允許應用程序測量系統負載(so it could behave appropriately)(poll()也提供了同樣的系統負載 測量)。
?
Signal-per-fd
Signal-per-fd是由Chandra和Mosberger提出的對實時信號的一種改進,它可以減少甚至削除實 時信號的溢出通過oalescing redundant events。然而是它的性能并沒有epoll好. 論文(www.hpl.hp.com/techreports/2000/HPL-2000-174.html) 比較了它和select(),/dev/poll的性能.
Vitaly Luban在2001.5.18公布了一個實現Signal-per-fd的補丁; 授權見www.luban.org/GPL/gpl.html. (到2001.9,在很重的負載情況下仍然存在穩定性問題,利用dkftpbench測試在4500個用戶時將引發問題.
See Poller_sigfd (cc, h) for an example of how to use signal-per-fd interchangeably with many other readiness notification schemes.
3. 一個服務線程服務多個客戶端,使用異步I/O
該方法目前還沒有在Unix上普遍的使用,可能因為很少的操作系統支持異步I/O,或者因為它需 要重新修改應用程序(rethinking your applications)。 在標準Unix下,異步I/O是由“aio_”接口 提供的,它把一個信號和值與每一個I/O操作關聯起來。信號和其值的隊列被有效地分配到用戶的 進程上。異步I/O是POSIX 1003.1b實時標準的擴展,也屬于Single Unix Specification,version 2.
AIO使用的是邊緣觸發的完成時通知,例如,當一個操作完成時信號就被加入隊列(也可以使用 水平觸發的完成時通知,通過調用aio_suspend()即可, 不過我想很少人會這么做).
glibc 2.1和后續版本提供了一個普通的實現,僅僅是為了兼容標準,而不是為了獲得性能上的提高。
Ben LaHaise編寫的Linux AIO實現合并到了2.5.32的內核中,它并沒有采用內核線程,而是使 用了一個高效的underlying api,但是目前它還不支持套接字(2.4內核也有了AIO的補丁,不過 2.5/2.6的實現有一定程序上的不同)。更多信息如下:
The page “Kernel Asynchronous I/O (AIO) Support for Linux” which tries to tie together all info about the 2.6 kernel’s implementation of AIO (posted 16 Sept 2003)
Round 3: aio vs /dev/epoll by Benjamin C.R. LaHaise (presented at 2002 OLS)
Asynchronous I/O Suport in Linux 2.5, by Bhattacharya, Pratt, Pulaverty, and Morgan, IBM; presented at OLS ‘2003
Design Notes on Asynchronous I/O (aio) for Linux by Suparna Bhattacharya — compares Ben’s AIO with SGI’s KAIO and a few other AIO projects
Linux AIO home page - Ben’s preliminary patches, mailing list, etc.
linux-aio mailing list archives
libaio-oracle - library implementing standard Posix AIO on top of libaio. First mentioned by Joel Becker on 18 Apr 2003.
Suparma建議先看看AIO的API.
RedHat AS和Suse SLES都在2.4的內核中提供了高性能的實現,與2.6的內核實現相似,但并不完全一樣。
2006.2,在網絡AIO有了一個新的嘗試,具體請看Evgeniy Polyakov的基于kevent的AIO.
1999, SGI為Linux實現了一個高速的AIO< /a>,在到1.1版本時,據說可以很好的工作于磁盤I/O和網 絡套接字,且使用了內核線程。目前該實現依然對那些不能等待Ben的AIO套接字支持的人來說是 很有用的。
O’Reilly 的“POSIX.4: Programming for the Real World”一書對aio做了很好的介紹.
這里 有一個指南介紹了早期的非標準的aio實現,可以看看,但是請記住你得把”aioread”轉換為”aio_read”。
注意AIO并沒有提供無阻塞的為磁盤I/O打開文件的方法,如果你在意因打開磁盤文件而引起 sleep的話,Linus建議 你在另外一個線程中調用open()而不是把希望寄托在對aio_open()系統調用上。
在Windows下,異步I/O與術語”重疊I/O”和”IOCP”(I/O Completion Port,I/O完成端口)有一定聯系。Microsoft的IOCP結合了 先前的如異步I/O(如aio_write)的技術,把事件完成的通知進行排隊(就像使用了aio_sigevent字段的aio_write),并且它 為了保持單一IOCP線程的數量從而阻止了一部分請求。(Microsoft’s IOCP combines techniques from the prior art like asynchronous I/O (like aio_write) and queued completion notification (like when using the aio_sigevent field with aio_write) with a new idea of holding back some requests to try to keep the number of running threads associated with a single IOCP constant.) 更多信息請看 Mark russinovich在sysinternals.com上的文章 Inside I/O Completion Ports, Jeffrey Richter的書”Programming Server-Side Applications for Microsoft Windows 2000″ (Amazon, MSPress), U.S. patent #06223207, or MSDN.
4. 一個服務線程服務一個客戶端,使用阻塞I/O
… 讓read()和write()阻塞. 這樣不好的地方在于需要為每個客戶端使用一個完整的棧,從而比較浪費內存。 許多操作系統仍在處理數百個線程時存在一定的問題。如果每個線程使用2MB的棧,那么當你在32位的機器上運行 512(2^30 / 2^21=512)個線程時,你就會用光所有的1GB的用戶可訪問虛擬內存(Linux也是一樣運行在x86上的)。 你可以減小每個線程所擁有的棧內存大小,但是由于大部分線程庫在一旦線程創建后就不能增大線程棧大小,所以這樣做 就意味著你必須使你的程序最小程度地使用內存。當然你也可以把你的程序運行在64位的處理器上去。
Linux,FreeBSD和Solaris系統的線程庫一直在更新,64位的處理器也已經開始在大部分的用戶中所使用。 也許在不遠的將來,這些喜歡使用一個線程來服務一個客戶端的人也有能力服務于10000個客戶了。 但是在目前,如果你想支持更多的客戶,你最好還是使用其它的方法。
For an unabashedly pro-thread viewpoint, see Why Events Are A Bad Idea (for High-concurrency Servers) by von Behren, Condit, and Brewer, UCB, presented at HotOS IX. Anyone from the anti-thread camp care to point out a paper that rebuts this one?? 
LinuxThreads
LinuxTheads 是標準Linux線程庫的命名。 它從glibc2.0開始已經集成在glibc庫中,并且高度兼容Posix標準,不過在性能和信號的支持度上稍遜一籌。
NGPT: Next Generation Posix Threads for Linux下一代LinuxPosix線程
NGPT是一個由IBM發起的項目,其目的是提供更好的Posix兼容的Linux線程支持。 現在已到2.2穩定版,并且運行良好…但是NGPT team 公布 他們正在把NGPT的代碼基改為support-only模式,因為他們覺得這才是支持社區長久運行的最好的方式。 NGPT小組將繼續改進Linux的線程支持,但主要關注NPTL方面。 (Kudos to the NGPT team for their good work and the graceful way they conceded to NPTL.)
NPTL: Native Posix Thread Library for Linux(Linux本地Posix線程庫)
NPTL是由 Ulrich Drepper ( glibc的主要維護人員)和 Ingo Molnar發起的項目,目的是提供world-class的Posix Linux線程支持。
2003.10.5,NPTL作為一個add-on目錄(就像linuxthreads一樣)被合并到glibc的cvs樹中,所以很有可能隨glibc的下一次release而 一起發布。
Red Hat 9是最早的包含NPTL的發行版本(對一些用戶來說有點不太方便,但是必須有人來打破這沉默[break the ice]…)
NPTL links:
NPTL討論的郵件列表
NPTL源碼
NPTL的最初發表
最初的描述NPTL目標的白皮書
修改的NPTL的最后設計的白皮書
Ingo Molnar最初的基準測試表明可以處理10^6個線程
Ulrich的基準測試 比較了LinuxThreads,NPTL和IBM的NGPT的各自性能,結果看來NPTL比NGPT快的多。
這是我嘗試寫的描述NPTL歷史的文章(也可以參考Jerry Cooperstein的文章):
2002.3,NGPT小組的Bill Abt,glibc的維護者Ulrich Drepper 和其它人召開了個會議來探討LinuxThreads的發展,會議的一個idea就是要改進mutex的性能。 Rusty Russell 等人 隨后實現了 fast userspace mutexes (futexes), (如今已在NGPT和NPTL中應用了)。 與會的大部分人都認為NGPT應該合并到glibc中。
然而Ulrich Drepper并不怎么喜歡NGPT,他認為他可以做得更好。 (對那些曾經想提供補丁給glibc的人來說,這應該不會令他們感到驚訝:-) 于是在接下來的幾個月里,Ulrich Drepper, Ingo Molnar和其它人致力于glibc和內核的改變,然后就弄出了 Native Posix Threads Library (NPTL). NPTL使用了NGPT設計的所有內核改進(kernel enhancement),并且采用了幾個最新的改進。 Ingo Molnar描述了 一下的幾個內核改進:
NPTL使用了三個由NGPT引入的內核特征: getpid()返回PID,CLONE_THREAD和futexes; NPTL還使用了(并依賴)也是該項目的一部分的一個更為wider的內核特征集。 一些由NGPT引入內核的items也被修改,清除和擴展,例如線程組的處理(CLONE_THREAD). [the CLONE_THREAD changes which impacted NGPT's compatibility got synced with the NGPT folks, to make sure NGPT does not break in any unacceptable way.]
這些為NPTL開發的并且后來在NPTL中使用的內核特征都描述在設計白皮書中, http://people.redhat.com/drepper/nptl-design.pdf …
A short list: TLS support, various clone extensions (CLONE_SETTLS, CLONE_SETTID, CLONE_CLEARTID), POSIX thread-signal handling, sys_exit() extension (release TID futex upon VM-release), the sys_exit_group() system-call, sys_execve() enhancements and support for detached threads.
There was also work put into extending the PID space - eg. procfs crashed due to 64K PID assumptions, max_pid, and pid allocation scalability work. Plus a number of performance-only improvements were done as well.
In essence the new features are a no-compromises approach to 1:1 threading - the kernel now helps in everything where it can improve threading, and we precisely do the minimally necessary set of context switches and kernel calls for every basic threading primitive.
NGPT和NPTL的一個最大的不同就是NPTL是1:1的線程模型,而NGPT是M:N的編程模型(具體請看下面). 盡管這樣, Ulrich的最初的基準測試 還是表明NPTL比NGPT快很多。(NGPT小組期待查看Ulrich的測試程序來核實他的結果.)
FreeBSD線程支持
FreeBSD支持LinuxThreads和用戶空間的線程庫。同樣,M:N的模型實現KSE在FreeBSD 5.0中引入。 具體請查看www.unobvious.com/bsd/freebsd-threads.html.
2003.3.25, Jeff Roberson 發表于freebsd-arch:
… 感謝Julian, David Xu, Mini, Dan Eischen,和其它的每一位參加了KSE和libpthread開發的成員所提供的基礎, Mini和我已經開發出了一個1:1模型的線程實現,它可以和KSE并行工作而不會帶來任何影響。It actually helps bring M:N threading closer by testing out shared bits. …
And 2006.7, Robert Watson提議1:1的線程模型應該為FreeBSD 7.x的默認實現:
我知道曾經討論過這個問題,但是我認為隨著7.x的向前推進,這個問題應該重新考慮。 在很多普通的應用程序和特定的基準測試中,libthr明顯的比libpthread在性能上要好得多。 libthr是在我們大量的平臺上實現的,而libpthread卻只有在幾個平臺上。 最主要的是因為我們使得Mysql和其它的大量線程的使用者轉換到”libthr”,which is suggestive, also! … 所以strawman提議:讓libthr成為7.x上的默認線程庫。 
NetBSD線程支持
根據Noriyuki Soda的描述:
內核支持M:N線程庫是基于調度程序激活模型,合并于2003.1.18當時的NetBSD版本中。
詳情請看Nathan J. Williams, Wasabi Systems, Inc.在2002年的FREENIX上的演示 An Implementation of Scheduler Activations on the NetBSD Operating System。
Solaris線程支持
Solaris的線程支持還在進一步提高evolving… 從Solaris 2到Solaris 8,默認的線程庫使用的都是M:N模型, 但是Solaris 9卻默認使用了1:1線程模型. 查看Sun多線程編程指南 和Sun的關于Java和Solaris線程的note.
Java在JDK 1.3.x及更早的線程支持
大家都知道,Java一直到JDK1.3.x都沒有支持任何處理網絡連接的方法,除了一個線程服務一個客戶端的模型之外。 Volanomark是一個不錯的微型測試程序,可以用來測量在 某個時候不同數目的網絡連接時每秒鐘的信息吞吐量。在2003.5, JDK 1.3的實現實際上可以同時處理10000個連接,但是性能卻嚴重下降了。 從Table 4 可以看出JVMs可以處理10000個連接,但是隨著連接數目的增長性能也逐步下降。
Note: 1:1 threading vs. M:N threading
在實現線程庫的時候有一個選擇就是你可以把所有的線程支持都放到內核中(也就是所謂的1:1的模型),也可以 把一些線程移到用戶空間上去(也就是所謂的M:N模型)。從某個角度來說, M:N被認為擁有更好的性能,但是由于很難被正確的編寫, 所以大部分人都遠離了該方法。
為什么Ingo Molnar相對于M:N更喜歡1:1
Sun改為1:1的模型
NGPT是Linux下的M:N線程庫.
Although Ulrich Drepper計劃在新的glibc線程庫中使用M:N的模型, 但是還是選用了1:1的模型.
MacOSX 也將使用1:1的線程.
FreeBSD和 NetBSD 仍然將使用M:N線程,FreeBSD 7.0也傾向于使用1:1的線程(見上面描述),可能M:N線程的擁護者最后證明它是錯誤的。
5. 把服務代碼編譯進內核
Novell和Microsoft都宣稱已經在不同時期完成了該工作,至少NFS的實現完成了該工作。 khttpd在Linux下為靜態web頁面完成了該工作, Ingo Molnar完成了“TUX” (Threaded linUX webserver) ,這是一個Linux下的快速的可擴展的內核空間的HTTP服務器。 Ingo在2000.9.1宣布 alpha版本的TUX可以在 ftp://ftp.redhat.com/pub/redhat/tux下載, 并且介紹了如何加入其郵件列表來獲取更多信息。
在Linux內核的郵件列表上討論了該方法的好處和缺點,多數人認為不應該把web服務器放進內核中, 相反內核加入最小的鉤子hooks來提高web服務器的性能,這樣對其它形式的服務器就有益。 具體請看 Zach Brown的討論 對比用戶級別和內核的http服務器。 在2.4的linux內核中為用戶程序提供了足夠的權力(power),就像X15 服務器運行的速度和TUX幾乎一樣,但是它沒有對內核做任何改變。
=============================================
???????????????????????? 此文源自網絡,第一作者未知
============================================
總結
以上是生活随笔為你收集整理的The C10K problem原文翻译的全部內容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: 115级55w战力能不能通关花园深处?
 - 下一篇: 高丽参多少钱一斤