現在的web服務器須要同時處理一萬個以上的客戶端了,難道不是嗎?畢竟現在的網絡是個big place了。php
如今的計算機也很強大了,你只須要花大概$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!)css
在1999年最繁忙的ftp站點,cdrom.com,儘管有G比特的網絡帶寬,卻也只能同時處理10000個 客戶端。在2001年,一樣的速度能夠被幾個ISP服務商所提供,他們預期該趨勢會由於大量的商業 用戶而變得愈來愈廣泛。html
目前的瘦客戶端模型也開始又變得流行起來了--服務器運行在Internet上,爲數千個客戶端服務。java
基於以上一些考慮,這裏就配置操做系統或者編寫支持數千個網絡客戶端的代碼問題提出一些 注意點,該論題是基於類Unix操做系統的--該系統是個人我的愛好,固然Windows也有佔有一席之地。node
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.)linux
若是你尚未讀過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 isBuilding Scalable Web Sites by Cal Henderson.)git
如下所列的爲幾個包裝好的庫,它們概要了幾中常見的技巧,而且可使你的代碼與具體操做 系統隔離,從而具備更好的移植性。web
網絡軟件設計者每每有不少種選擇,如下列出一些:sql
下面的五種方式應該是最經常使用的了。apache
...把網絡句柄設置爲非阻塞模型,而後使用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.
在非阻塞套接字的集合中,關於單一線程是如何告知哪一個套接字是準備就緒的,如下列出了幾 種方法:
See Poller_select (cc, h) for an example of how to use select() interchangeably with other readiness notification schemes.
有些操做系統(如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的背後思想就是利用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()便可以水平觸發,也能夠邊緣觸發,具體請看下面.
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可使得應用程序得到「文件描述符已就緒」的通知:
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()的例程和庫:
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.
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()。
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()也提供了一樣的系統負載 測量)。
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.
該方法目前尚未在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的實現有必定程序上的不一樣)。更多信息以下:
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.
... 讓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? :-)
LinuxTheads 是標準Linux線程庫的命名。 它從glibc2.0開始已經集成在glibc庫中,而且高度兼容Posix標準,不過在性能和信號的支持度上稍遜一籌。
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是由 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歷史的文章(也能夠參考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支持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上的默認線程庫。
根據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的線程支持還在進一步提升evolving... 從Solaris 2到Solaris 8,默認的線程庫使用的都是M:N模型, 可是Solaris 9卻默認使用了1:1線程模型. 查看Sun多線程編程指南 和Sun的關於Java和Solaris線程的note.
你們都知道,Java一直到JDK1.3.x都沒有支持任何處理網絡鏈接的方法,除了一個線程服務一個客戶端的模型以外。 Volanomark是一個不錯的微型測試程序,能夠用來測量在 某個時候不一樣數目的網絡鏈接時每秒鐘的信息吞吐量。在2003.5, JDK 1.3的實現實際上能夠同時處理10000個鏈接,可是性能卻嚴重降低了。 從Table 4 能夠看出JVMs能夠處理10000個鏈接,可是隨着鏈接數目的增加性能也逐步降低。
在實現線程庫的時候有一個選擇就是你能夠把全部的線程支持都放到內核中(也就是所謂的1:1的模型),也能夠 把一些線程移到用戶空間上去(也就是所謂的M:N模型)。從某個角度來講, M:N被認爲擁有更好的性能,可是因爲很難被正確的編寫, 因此大部分人都遠離了該方法。
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幾乎同樣,可是它沒有對內核作任何改變。
Richard Gooch曾經寫了一篇討論I/O選項的論文。
在2001, Tim Brecht和MMichal Ostrowski爲使用簡單的select的服務器 作了各類策略的測度 測試的數據值得看一看。
在2003, Tim Brecht發表了 userver的源碼, 該服務器是整合了Abhishek Chandra, David Mosberger, David Pariag和Michal Ostrowski所寫的幾個服務器而成的, 可使用select(), poll(), epoll()和sigio.
回到1999.3, Dean Gaudet發表:
我一直在問「爲何大家不使用基於select/event的模型,它明顯是最快的。」...
他們的理由是「太難理解了,而且其中關鍵部分(payoff)不清晰」,可是幾個月後,當該模型變得易懂時人們就開始願意使用它了。
Mark Russinovich寫了 一篇評論和 文章討論了在2.2的linux內核只可以I/O策略問題。 儘管某些地方彷佛有點錯誤,不過仍是值得去看。特別是他認爲Linux2.2的異步I/O (請看上面的F_SETSIG) 並無在數據準備好時通知用戶進程,而只有在新的鏈接到達時纔有。 這看起來是一個奇怪的誤解。 還能夠看看 早期的一些comments, Ingo Molnar在1999.4.30所舉的反例, Russinovich在1999.5.2的comments, Alan Cox的 反例,和各類 linux內核郵件. 我懷疑他想說的是Linux不支持異步磁盤I/O,這在過去是正確的,可是如今SGI已經實現了KAIO,它已再也不正確了。
查看頁面 sysinternals.com和 MSDN瞭解一下「完成端口」, 聽說它是NT中獨特的技術, 簡單說,win32的"重疊I/O"被認爲是過低水平而不方面使用,「完成端口」是提供了完成事件隊列的封裝,再加上魔法般的調度, 經過容許更多的線程來得到完成事件若是該端口上的其它已得到完成事件的線程處於睡眠中時(可能正在處理阻塞I/O),從而能夠保持運行線程數目恆定(scheduling magic that tries to keep the number of running threads constant by allowing more threads to pick up completion events if other threads that had picked up completion events from this port are sleeping (perhaps doing blocking I/O).
在1999.9,在linux內核郵件列表上曾有一次很是有趣的討論,討論題目爲 "15,000 Simultaneous Connections" (而且延續到第二週). Highlights:
Interesting reading!
set kern.maxfiles=XXXXwhere XXXX is the desired system limit on file descriptors, and reboot. Thanks to an anonymous reader, who wrote in to say he'd achieved far more than 10000 connections on FreeBSD 4.3, and says
"FWIW: You can't actually tune the maximum number of connections in FreeBSD trivially, via sysctl.... You have to do it in the /boot/loader.conf file.Another reader says
The reason for this is that the zalloci() calls for initializing the sockets and tcpcb structures zones occurs very early in system startup, in order that the zone be both type stable and that it be swappable.
You will also need to set the number of mbufs much higher, since you will (on an unmodified kernel) chew up one mbuf per connection for tcptempl structures, which are used to implement keepalive."
"As of FreeBSD 4.4, the tcptempl structure is no longer allocated; you no longer have to worry about one mbuf being chewed up per connection."See also:
"In OpenBSD, an additional tweak is required to increase the number of open filehandles available per process: the openfiles-cur parameter in /etc/login.conf needs to be increased. You can change kern.maxfiles either with sysctl -w or in sysctl.conf but it has no effect. This matters because as shipped, the login.conf limits are a quite low 64 for nonprivileged processes, 128 for privileged."
echo 32768 > /proc/sys/fs/file-maxincreases the system limit on open files, and
ulimit -n 32768increases the current process' limit.
On 2.2.x kernels,
echo 32768 > /proc/sys/fs/file-max echo 65536 > /proc/sys/fs/inode-maxincreases the system limit on open files, and
ulimit -n 32768increases the current process' limit.
I verified that a process on Red Hat 6.0 (2.2.5 or so plus patches) can open at least 31000 file descriptors this way. Another fellow has verified that a process on 2.2.12 can open at least 90000 file descriptors this way (with appropriate limits). The upper bound seems to be available memory.
Stephen C. Tweedie posted about how to set ulimit limits globally or per-user at boot time using initscript and pam_limit.
In older 2.2 kernels, though, the number of open files per process is still limited to 1024, even with the above changes.
See also Oskar's 1998 post, which talks about the per-process and system-wide limits on file descriptors in the 2.0.36 kernel.
On any architecture, you may need to reduce the amount of stack space allocated for each thread to avoid running out of virtual memory. You can set this at runtime with pthread_attr_init() if you're using pthreads.
See also Volano's detailed instructions for raising file, thread, and FD_SET limits in the 2.2 kernel. Wow. This document steps you through a lot of stuff that would be hard to figure out yourself, but is somewhat dated.
Up through JDK 1.3, Java's standard networking libraries mostly offered the one-thread-per-client model. There was a way to do nonblocking reads, but no way to do nonblocking writes.
In May 2001, JDK 1.4 introduced the package java.nio to provide full support for nonblocking I/O (and some other goodies). See the release notes for some caveats. Try it out and give Sun feedback!
HP's java also includes a Thread Polling API.
In 2000, Matt Welsh implemented nonblocking sockets for Java; his performance benchmarks show that they have advantages over blocking sockets in servers handling many (up to 10000) connections. His class library is called java-nbio; it's part of the Sandstorm project. Benchmarks showing performance with 10000 connectionsare available.
See also Dean Gaudet's essay on the subject of Java, network I/O, and threads, and the paper by Matt Welsh on events vs. worker threads.
Before NIO, there were several proposals for improving Java's networking APIs:
According to a note from Noriyuki Soda:
Sending side zero-copy is supported since NetBSD-1.6 release by specifying "SOSEND_LOAN" kernel option. This option is now default on NetBSD-current (you can disable this feature by specifying "SOSEND_NO_LOAN" in the kernel option on NetBSD_current). With this feature, zero-copy is automatically enabled, if data more than 4096 bytes are specified as data to be sent.
A zero-copy implementation of sendfile() is on its way for the 2.4 kernel. See LWN Jan 25 2001.
One developer using sendfile() with Freebsd reports that using POLLWRBAND instead of POLLOUT makes a big difference.
Solaris 8 (as of the July 2001 update) has a new system call 'sendfilev'. A copy of the man page is here.. The Solaris 8 7/01 release notes also mention it. I suspect that this will be most useful when sending to a socket in blocking mode; it'd be a bit of a pain to use with a nonblocking socket.
See LWN Jan 25 2001 for a summary of some very interesting discussions on linux-kernel about TCP_CORK and a possible alternative MSG_MORE.
"I've compared the raw performance of a select-based server with a multiple-process server on both FreeBSD and Solaris/x86. On microbenchmarks, there's only a marginal difference in performance stemming from the software architecture. The big performance win for select-based servers stems from doing application-level caching. While multiple-process servers can do it at a higher cost, it's harder to get the same benefits on real workloads (vs microbenchmarks). I'll be presenting those measurements as part of a paper that'll appear at the next Usenix conference. If you've got postscript, the paper is available athttp://www.cs.rice.edu/~vivek/flash99/"
For Linux, it looks like kernel bottlenecks are being fixed constantly. See Linux Weekly News, Kernel Traffic, the Linux-Kernel mailing list, and my Mindcraft Redux page.
In March 1999, Microsoft sponsored a benchmark comparing NT to Linux at serving large numbers of http and smb clients, in which they failed to see good results from Linux. See also my article on Mindcraft's April 1999 Benchmarks for more info.
See also The Linux Scalability Project. They're doing interesting work, including Niels Provos' hinting poll patch, and some work on the thundering herd problem.
See also Mike Jagdis' work on improving select() and poll(); here's Mike's post about it.
Two tests in particular are simple, interesting, and hard:
Jef Poskanzer has published benchmarks comparing many web servers. Seehttp://www.acme.com/software/thttpd/benchmarks.html for his results.
I also have a few old notes about comparing thttpd to Apache that may be of interest to beginners.
Chuck Lever keeps reminding us about Banga and Druschel's paper on web server benchmarking. It's worth a read.
IBM has an excellent paper titled Java server benchmarks [Baylor et al, 2000]. It's worth a read.
$Log: c10k.html,v $ Revision 1.212 2006/09/02 14:52:13 dank added asio Revision 1.211 2006/07/27 10:28:58 dank Link to Cal Henderson's book. Revision 1.210 2006/07/27 10:18:58 dank Listify polyakov links, add Drepper's new proposal, note that FreeBSD 7 might move to 1:1 Revision 1.209 2006/07/13 15:07:03 dank link to Scale! library, updated Polyakov links Revision 1.208 2006/07/13 14:50:29 dank Link to Polyakov's patches Revision 1.207 2003/11/03 08:09:39 dank Link to Linus's message deprecating the idea of aio_open Revision 1.206 2003/11/03 07:44:34 dank link to userver Revision 1.205 2003/11/03 06:55:26 dank Link to Vivek Pei's new Flash paper, mention great specweb99 score
Copyright 1999-2006 Dan Kegel
dank@kegel.com
Last updated: 2 Sept 2006
[Return to www.kegel.com]