C10K問題渣翻譯

The C10K problemphp

[Help save the best Linux news source on the web -- subscribe to Linux Weekly News! ]css

It's time for web servers to handle ten thousand clients simultaneously, don't you think? After all, the web is a big place now.html

And computers are big, too. You can buy a 1000MHz machine with 2 gigabytes of RAM and an 1000Mbit/sec Ethernet card for $1200 or so. Let's see - at 20000 clients, that's 50KHz, 100Kbytes, and 50Kbits/sec per client. It shouldn't take any more horsepower than that to take four kilobytes from the disk and send them to the network once a second for each of twenty thousand clients. (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!) So hardware is no longer the bottleneck.java

如今已經到了讓web服務器同時處理上萬個鏈接的時候了,不是嗎?畢竟,互聯網如今是一個巨大的網絡了。node

同時如今的電腦也愈來愈強大了。你能夠花費大約1200美圓買到一臺主頻1000MHZ的電腦配上2G內存和1000Mbit/sec的網卡。讓咱們看看,當其處理20000個客戶鏈接時,它將給每個客戶鏈接分配的資源是50KHz100KB50kbits/sec。那麼沒有什麼爲20000個客戶鏈接中的每個每秒鐘內從磁盤上讀取4KB數據並將其發送到網絡上時更耗資源的了。(順便說一下,那將爲每個客戶耗費0.08美圓。那些爲每個客戶花費100美圓的操做系統花費就開始顯得有些昂貴了!)因此硬件已再也不是瓶頸了。linux

In 1999 one of the busiest ftp sites, cdrom.com, actually handled 10000 clients simultaneously through a Gigabit Ethernet pipe. As of 2001, that same speed is now being offered by several ISPs , who expect it to become increasingly popular with large business customers.ios

在1999年,最繁忙的一個ftp網站,cdrom.com,事實上經過千兆以太網pipe已同時處理上萬個鏈接了。到了2001年,一樣的速度能夠被一些ISP提供了,他們預期該趨勢將會由於大量的商業用戶而變得愈來愈普通。git

And the thin client model of computing appears to be coming back in style -- this time with the server out on the Internet, serving thousands of clients.web

With that in mind, here are a few notes on how to configure operating systems and write code to support thousands of clients. The discussion centers around Unix-like operating systems, as that's my personal area of interest, but Windows is also covered a bit.sql

目前thin客戶端模式又變得流行起來了——服務器運行在Internet上併爲數千個客戶提供服務。考慮到這一點,這有一些怎樣配置操做系統和編寫代碼來支持數千個客戶鏈接的筆記。這些討論主要圍繞類Unix系統,那是我我的的興趣範圍,可是也會有一些windows相關的內容。

Contents

I/O Strategies

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

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.

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

 

Related Sites

In October 2003, Felix von Leitner put together an excellent web page and presentation about network scalability, complete with benchmarks comparing various networking system calls and operating systems. One of his observations is that the 2.6 Linux kernel really does beat the 2.4 kernel, but there are many, many good graphs that will give the OS developers food for thought for some time. (See also the Slashdot comments; it'll be interesting to see whether anyone does followup benchmarks improving on Felix's results.)

在2003年十月,Felix von Leitner發表了一個矚目的網頁和關於網絡可擴展性的介紹,完成了不一樣的操做系統和相關網絡系統調用的性能基準對比,他的其中的一個結論是linux 2.6內核擊敗了linux2.4內核,固然還有許多的圖片在一段時間內能夠給OS的開發者一些想法。(看一下Slashdot的評論;看看人們是否遵循了Felix提供的基準來提升性能也是一件頗有趣的事情)

Book to Read First

If you haven't read it already, go out and get a copy of Unix Network Programming : Networking Apis: Sockets and Xti (Volume 1) by the late W. Richard Stevens. It describes many of the I/O strategies and pitfalls related to writing high-performance servers. It even talks about the 'thundering herd' problem. And while you're at it, go read Jeff Darcy's notes on high-performance server design .

(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.)

若是你尚未讀過最新的W. Richard Stevens的UNP Vol1,你應該立刻出去並去得到一份copy。它描述了許多I/O策略和編寫高性能服務器的相關陷阱。它甚至討論了「驚羣」問題。你也能夠閱讀JeffDarcy關於高性能服務器設計的筆記。(若是你是須要使用而不是編寫一個web服務器,那麼另外一本對你有用的書是Cal Henderson的Building Scalable Web Sites

(注:「驚羣」問題——簡單說來,多線程/多進程(linux下線程進程也沒多大區別)等待同一個socket事件,當這個事件發生時,這些線程/進程被同時喚醒,就是驚羣。能夠想見,效率很低下,許多進程被內核從新調度喚醒,同時去響應這一個事件,固然只有一個進程能處理事件成功,其餘的進程在處理該事件失敗後從新休眠(也有其餘選擇)。這種性能浪費現象就是驚羣。

I/O frameworks

Prepackaged libraries are available that abstract some of the techniques presented below, insulating your code from the operating system and making it more portable.

一下爲幾個已經包裝好的庫,它們歸納了一些特定技術並使你的代碼和操做系統相隔離,使其具備更好的可移植性。

  • ACE , a heavyweight C++ I/O framework, contains object-oriented implementations of some of these I/O strategies and many other useful things. In particular, his Reactor is an OO way of doing nonblocking I/O, and Proactor is an OO way of doing asynchronous I/O.
  • ACE,一個重量級C++ I/O框架,包括了用面向對象實現的一些I/O策略和其餘有用的東西。特別的,他的Reactor是一個處理非阻塞I/OOO方法,Proactor是一個處理異步I/OOO方法。
  • ASIO is an C++ I/O framework which is becoming part of the Boost library. It's like ACE updated for the STL era.
  • ASIO,一個正成爲Boost庫的部分的C++ I/O框架,就像ACESTL時代的更新。
  • libevent is a lightweight C I/O framework by Niels Provos. It supports kqueue and select, and soon will support poll and epoll. It's level-triggered only, I think, which has both good and bad sides. Niels has a nice graph of time to handle one event as a function of the number of connections. It shows kqueue and sys_epoll as clear winners.
  • Libevent,是一個由Niels Provos完成的輕量級C I/O框架。它支持kqueueselect,而且不久將支持pollepoll。他只支持水平觸發機制,我認爲這既有好的一面也有壞的一面。Niels給出了一張圖 來講明時間和鏈接數目在處理一個事件上的功能,從圖上能夠看出kqueuesys_epoll明顯勝出。
  • My own attempts at lightweight frameworks (sadly, not kept up to date): 
  • 本人嘗試過的一些輕量級框架(惋惜的是沒有堅持至今)

Poller is a lightweight C++ I/O framework that implements a level-triggered readiness API using whatever underlying readiness API you want (poll, select, /dev/poll, kqueue, or sigio). It's useful forbenchmarks that compare the performance of the various APIs. This document links to Poller subclasses below to illustrate how each of the readiness APIs can be used.

o Poller 是一個輕量級的C++ I/O框架,它使用任何一種準備就緒API(poll, select, /dev/poll, kqueue, sigio)實現水平觸發準備就緒API。以其它不一樣的API爲基準 ,Poller的性能 好得多。該連接文檔的下面一部分說明了如何使用這些準備就緒API

rn is a lightweight C I/O framework that was my second try after Poller. It's lgpl (so it's easier to use in commercial apps) and C (so it's easier to use in non-C++ apps). It was used in some commercial products.

o rn 是一個輕量級的C I/O框架,也是我繼Poller後的第二個框架。該框架能夠很容易的被用 於商業應用中,也容易的適用於非C++應用中。它現在已經在幾個商業產品中使用。

  • Matt Welsh wrote a paper in April 2000 about how to balance the use of worker thread and event-driven techniques when building scalable servers. The paper describes part of his Sandstorm I/O framework.
  • Matt Welsh在2000年四月寫了一篇關於當搭建可擴展服務器時怎樣平衡worker線程和事件驅動技術的論文。這篇論文描述了部分他的Sandstorm I/O框架。
  • Cory Nelson's Scale! library - an async socket, file, and pipe I/O library for Windows
  • Cory Nelson's Scale! libray——一個包含異步套接字,文件和管道I/Owindows庫。

I/O Strategies

Designers of networking software have many options. Here are a few:

網絡軟件的設計者有許多的選擇,下面列出一些:

  • Whether and how to issue multiple I/O calls from a single thread 
  • 是否和怎樣在單線程裏處理多個的I/O調用

o Don't; use blocking/synchronous calls throughout, and possibly use multiple threads or processes to achieve concurrency

o 不處理;從頭至尾使用阻塞的/同步的調用,若是可能使用多線程或多進程來實現併發。

o Use nonblocking calls (e.g. write() on a socket set to O_NONBLOCK) to start I/O, and readiness notification (e.g. poll() or /dev/poll) to know when it's OK to start the next I/O on that channel. Generally only usable with network I/O, not disk I/O.

o 使用非阻塞調用(例如在套接字上調用write()時先設置O_NONBLOCK)來讀取I/O,當I/O完成時發出通知(如poll()/dev/poll)從而開始下一個I/O.這種主要使用在網絡I/O上,而不是磁盤的I/O上。

o Use asynchronous calls (e.g. aio_write()) to start I/O, and completion notification (e.g. signals or completion ports) to know when the I/O finishes. Good for both network and disk I/O.

o 使用異步調用(如aio_write())來讀取I/O,當I/O完成時會發出通知(如信號或完成端口),能夠同時在網絡I/O和磁盤I/O上使用。

  • How to control the code servicing each client 

o one process for each client (classic Unix approach, used since 1980 or so)

o 每個進程服務一個客戶(經典的Unix方法,大約從1980開始就一直使用

o one OS-level thread handles many clients; each client is controlled by: 

o 一個內核線程處理多個客戶,每個客戶被下面的三者之一控制

§ a user-level thread (e.g. GNU state threads, classic Java with green threads)

§ 一個用戶線程(如GNU狀態線程,典型的Java Green線程

§ a state machine (a bit esoteric, but popular in some circles; my favorite)

§ 一個狀態機

§ a continuation (a bit esoteric, but popular in some circles)

§ 一個continuation 

o one OS-level thread for each client (e.g. classic Java with native threads)

o 一個內核線程服務一個客戶

o one OS-level thread for each active client (e.g. Tomcat with apache front end; NT completion ports; thread pools)

o 一個內核線程服務一個活動客戶(如Tomcat with apache front end; NT完成端口;線程池)

  • Whether to use standard O/S services, or put some code into the kernel (e.g. in a custom driver, kernel module, or VxD)
  • 是否使用標準的操做系統服務,仍是把一些代碼放入內核中(如自定義驅動,內核模塊,VxD)。

The following five combinations seem to be popular:

Serve many clients with each thread, and use nonblocking I/O and level-triggered readiness notification

Serve many clients with each thread, and use nonblocking I/O and readiness change notification

Serve many clients with each server thread, and use asynchronous I/O

serve one client with each server thread, and use blocking I/O

Build the server code into the kernel

1. Serve many clients with each thread, and use nonblocking I/O and level-triggeredreadiness notification

... set nonblocking mode on all network handles, and use select() or poll() to tell which network handle has data waiting. This is the traditional favorite. With this scheme, the kernel tells you whether a file descriptor is ready, whether or not you've done anything with that file descriptor since the last time the kernel told you about it. (The name 'level triggered' comes from computer hardware design; it's the opposite of 'edge triggered' . Jonathon Lemon introduced the terms in his BSDCON 2000 paper on kqueue() .)

將全部的網絡句柄設置成非阻塞模式而且使用selectpoll來告知哪個句柄如今有數據等待處理。這是傳統的處理方法。使用這種策略,內核將告訴你一個文件描述符是否可準備好被讀寫,或者在上次內核通知你後你是否對文件描述符進行了處理(這種名爲「水平觸發」模式來自於計算機的硬件設計,它與「邊緣觸發」相對。)

Note: it's particularly important to remember that readiness notification from the kernel is only a hint; the file descriptor might not be ready anymore when you try to read from it. That's why it's important to use nonblocking mode when using readiness notification.

提示:請記住內核的就緒通知僅僅是一個提示,當你去讀一個文件描述符時可能描述符還沒就緒,這就是爲何當你使用就緒通知時將描述符設置成非阻塞模式如此的重要。

An important bottleneck in this method is that read() or sendfile() from disk blocks if the page is not in core at the moment; setting nonblocking mode on a disk file handle has no effect. Same thing goes for memory-mapped disk files. The first time a server needs disk I/O, its process blocks, all clients must wait, and that raw nonthreaded performance goes to waste. 

一個重要的性能瓶頸是當read()sendfile()要讀取存放在磁盤上的數據而這些數據卻不在內存中時,設置磁盤文件描述符爲非阻塞模式沒有任何幫助。一樣的事情也發生在內存映射磁盤文件上。當服務器第一次須要進行磁盤I/O時,它的進程將阻塞,全部的客戶必須等待同時最初的非線程性能將被浪費。
This is what asynchronous I/O is for, but on systems that lack AIO, worker threads or processes that do the disk I/O can also get around this bottleneck. One approach is to use memory-mapped files, and if mincore() indicates I/O is needed, ask a worker to do the I/O, and continue handling network traffic. Jef Poskanzer mentions that Pai, Druschel, and Zwaenepoel's 1999 Flash web server uses this trick; they gave a talk atUsenix '99 on it. It looks like mincore() is available in BSD-derived Unixes like FreeBSD and Solaris, but is not part of the Single Unix Specification . It's available as part of Linux as of kernel 2.3.51, thanks to Chuck Lever .

在缺乏AIO的系統上,異步I/O就是爲此準備的,進行磁盤I/O的工做線程或進程也能夠避開這個瓶頸。一種方法是使用內存映射文件,同時若是mincore()指明I/O是必須的話,命令工做線程去執行I/O,同時繼續處理網絡事件。Jef Poskanzer提到Pai, Druschel和Zwaenepoel的1999年的Flash web 服務器使用了這個方法;他們對此在Usenix '99上發表了一次演說,看上去就好像 FreeBSDSolaris 中提供了mincore()同樣,可是它並非Single Unix Specification的一部分,在Linux2.3.51 的內核中提供了該方法,感謝Chuck Lever

But in November 2003 on the freebsd-hackers list, Vivek Pei et al reported very good results using system-wide profiling of their Flash web server to attack bottlenecks. One bottleneck they found was mincore (guess that wasn't such a good idea after all) Another was the fact that sendfile blocks on disk access; they improved performance by introducing a modified sendfile() that return something like EWOULDBLOCK when the disk page it's fetching is not yet in core. (Not sure how you tell the user the page is now resident... seems to me what's really needed here is aio_sendfile().) 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.

2003.11的 freebsd-hackers list中,Vivek Pei上報了一個不錯的成果,他們利用系統剖析 工具剖析它們的Flash Web服務器,而後再攻擊其瓶頸。他們發現的其中一個瓶頸是mincore(猜想那畢竟不是一個好的想法)。另外一個是sendfile在訪問磁盤時阻塞了,他們經過引入一個修改後的sendfile來提升性能,當取磁盤頁不在內存中時sendfile將返回相似於EWOULDBLOCK之類的值(不肯定你怎樣告訴用戶磁盤頁如今在內存中……對於我,我須要的是aio_sendfile())。他們優化的最終結果是在SpecWeb99 使用一臺1GHZ/1GB FreeBSD box上得了800分。

There are several ways for a single thread to tell which of a set of nonblocking sockets are ready for I/O:

這裏有一些適用於單線程的方法來告知哪些非阻塞套接字準備好讀取I/O

  • The traditional select() 
    Unfortunately, select() is limited to FD_SETSIZE handles. This limit is compiled in to the standard library and user programs. (Some versions of the C library let you raise this limit at user app compile time.) 

不幸的是,select()FD_SETSIZE個數目的句柄所限制。這個限制被編譯到標準庫和用戶程序中(一些C庫版本讓你在應用編譯時提升這個限制)

See Poller_select (cc , h ) for an example of how to use select() interchangeably with other readiness notification schemes.

 

  • The traditional poll() 
    There is no hardcoded limit to the number of file descriptors poll() can handle, but it does get slow about a few thousand, since most of the file descriptors are idle at any one time, and scanning through thousands of file descriptors takes time. 

Poll()能處理的文件描述符數目沒有硬編碼限制,可是它在處理上千個描述符時會變慢,由於大多數文件描述符在某一時間段內處於閒置狀態,而且掃描一遍上千個描述符花費不少時間。

Some OS's (e.g. Solaris 8) speed up poll() et al by use of techniques like poll hinting, which wasimplemented and benchmarked by Niels Provos for Linux in 1999.

See Poller_poll (cc , h , benchmarks ) for an example of how to use poll() interchangeably with other readiness notification schemes.

 

  • /dev/poll 
    This is the recommended poll replacement for Solaris. 

這是在Solaris上推薦的poll的代替物

The idea behind /dev/poll is to take advantage of the fact that often poll() is called many times with the same arguments. With /dev/poll, you get an open handle to /dev/poll, and tell the OS just once what files you're interested in by writing to that handle; from then on, you just read the set of currently ready file descriptors from that handle.

/dev/poll的設計思想是利用這樣的事實,即poll()常用同一參數被調用不少次。使用/dev/poll時,你得到一個打開的/dev/poll句柄,且將你對什麼文件描述符感興趣寫入/dev/poll一次,並告知OS;今後,你就能夠從/dev/poll句柄獲得此時已經準備好讀寫的文件描述符集。

It appeared quietly in Solaris 7 (see patchid 106541 ) but its first public appearance was in Solaris 8 ;according to Sun , at 750 clients, this has 10% of the overhead of poll().

dev/poll Solaris 7(見補丁106541) 中就已經存在,不過在Solaris 8 中才公開現身。在750個客戶端的狀況下,僅花費poll()的十分之一開銷。

Various implementations of /dev/poll were tried on Linux, but none of them perform as well as epoll, and were never really completed. /dev/poll use on Linux is not recommended.

在linux上有/dev/poll的不一樣的實現,但它們都沒有達到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() 
    This is the recommended poll replacement for FreeBSD (and, soon, NetBSD). 

這是在FreeBSD系統上推薦使用的代替poll的方法。

See below. kqueue() can specify either edge triggering or level triggering.

2. Serve many clients with each thread, and use nonblocking I/O and readiness changenotification

Readiness change notification (or edge-triggered readiness notification) means you give the kernel a file descriptor, and later, when that descriptor transitions from not ready to ready , the kernel notifies you somehow. It then assumes you know the file descriptor is ready, and will not send any more readiness notifications of that type for that file descriptor until you do something that causes the file descriptor to no longer be ready (e.g. until you receive the EWOULDBLOCK error on a send, recv, or accept call, or a send or recv transfers less than the requested number of bytes).

就緒變化通知(或邊緣觸發就緒通知)意味着你給內核一個文件描述符,一段時間後,當這個描述符的狀態從沒就緒變爲就緒時,內核會經過某種方法通知你。內核通知完畢後會假設你已經知道文件描述符準備好了,以後不會對該描述符再發送任何就緒通知給你直到你作了某些事致使描述符再也不就緒(例如你在描述符上調用sendrecvaccept直到收到EWOULDBLOCK錯誤,或發送或接收了少於須要的字節數)。

When you use readiness change notification, you must be prepared for spurious events, since one common implementation is to signal readiness whenever any packets are received, regardless of whether the file descriptor was already ready.

當你使用就需改變通知,你必須爲僞事件有所準備,由於一個一般的實現是不管什麼時候只要有包到達就會發送就緒信號,而無論文件描述符是否準備就緒。

This is the opposite of "level-triggered " readiness 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. Nevertheless, I have found that edge-triggered readiness notification made programming nonblocking clients with OpenSSL easier, so it's worth trying.

這與「水平觸發」就緒通知所相對。這對編程錯誤少了幾分寬容,由於若是你遺漏了僅僅一個事件,該事件的鏈接將會永遠卡住。然而我發現邊緣觸發就緒通知機制使編寫使用OpenSSL的非阻塞客戶端更加容易,所以這值得一試。

[Banga, Mogul, Drusha '99] described this kind of scheme in 1999.

There are several APIs which let the application retrieve 'file descriptor became ready' notifications:

有幾種APIs可使得應用程序得到「文件描述符已就緒」的通知:

  • kqueue() This is the recommended edge-triggered poll replacement for FreeBSD (and, soon, NetBSD). 

kqueue() 這是在FreeBSD系統上推薦使用邊緣觸發的方法

FreeBSD 4.3 and later, and NetBSD-current as of Oct 2002 , support a generalized alternative to poll() called kqueue()/kevent() ; it supports both edge-triggering and level-triggering. (See also Jonathan Lemon's page and his BSDCon 2000 paper on kqueue() .)

FreeBSD 4.3及之後版本,NetBSD2002.10)都支持 kqueue()/kevent(), 支持邊沿觸發和水平觸發(請查看Jonathan Lemon 的網頁和他的BSDCon 2000關於kqueue的論文)。 

Like /dev/poll, you allocate a listening object, but rather than opening the file /dev/poll, you call kqueue() to allocate one. To change the events you are listening for, or to get the list of current events, you call kevent() on the descriptor returned by kqueue(). It can listen not just for socket readiness, but also for plain file readiness, signals, and even for I/O completion.

相似/dev/poll,你自行分配一個監聽對象,不是打開/dev/poll文件而是調用kqueue()來分配一個。爲了改變你監聽的事件或獲得當前事件的列表,你要在kqueue()返回的描述符上調用kevent()。它不只能監聽套接字上的就緒事件,還能監聽普通文件的就緒,信號甚至I/O完成事件。

Note: as of October 2000, the threading library on FreeBSD does not interact well with kqueue(); evidently, when kqueue() blocks, the entire process blocks, not just the calling thread.

提示:在2000.10FreeBSD的線程庫和kqueue()並不能一塊兒工做得很好,當kqueue()阻塞時, 那麼整個進程都將會阻塞,而不只僅是調用kqueue()的線程。

See Poller_kqueue (cc , h , benchmarks ) for an example of how to use kqueue() interchangeably with many other readiness notification schemes.

Examples and libraries using kqueue():

PyKQueue -- a Python binding for kqueue()

Ronald F. Guilmette's example echo server ; see also his 28 Sept 2000 post on freebsd.questions.

 

  • epoll 
    This is the recommended edge-triggered poll replacement for the 2.6 Linux kernel. 

這是Linux 2.6的內核中推薦使用的邊沿觸發poll

On 11 July 2001, Davide Libenzi proposed an alternative to realtime signals; his patch provides what he now calls /dev/epoll www.xmailserver.org/linux-patches/nio-improve.html . This is just like the realtime signal readiness notification, but it coalesces redundant events, and has a more efficient scheme for bulk event retrieval.

2001.7.11, Davide Libenzi提議了一個實時信號的可選方法,他稱之爲/dev/epoll, 該方法相似與實時信號就緒通知機制,可是結合了其它更多的事件,從而在大多數的事件獲取上擁有更高的效率。 

Epoll was merged into the 2.5 kernel tree as of 2.5.46 after its interface was changed from a special file in /dev to a system call, sys_epoll. A patch for the older version of epoll is available for the 2.4 kernel.

epoll在將它的接口從一個/dev下的指定文件改變爲系統調用sys_epoll後就合併到2.5版本的 Linux內核開發樹中,另外也提供了一個爲2.4老版本的內核可使用epoll的補丁。 

There was a lengthy debate about unifying epoll, aio, and other event sources on the linux-kernel mailing list around Halloween 2002. It may yet happen, but Davide is concentrating on firming up epoll in general first.

 

  • Polyakov's kevent (Linux 2.6+) News flash: On 9 Feb 2006, and again on 9 July 2006, Evgeniy Polyakov posted patches which seem to unify epoll and aio; his goal is to support network AIO. See: 

the LWN article about kevent

his July announcement

his kevent page

his naio page

some recent discussion

 

  • Drepper's New Network Interface (proposal for Linux 2.6+)
    At OLS 2006, Ulrich Drepper proposed a new high-speed asynchronous networking API. See: 

o his paper, "The Need for Asynchronous, Zero-Copy Network I/O "

his slides

LWN article from July 22

 

  • Realtime Signals 
    This is the recommended edge-triggered poll replacement for the 2.4 Linux kernel. 

The 2.4 linux kernel can deliver socket readiness events via a particular realtime signal. Here's how to turn this behavior on:

Linux2.4內核中推薦使用的邊沿觸發poll.2.4linux內核能夠經過實時信號來分派套接字事件,示例以下:

/* 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);

This sends that signal when a normal I/O function like read() or write() completes. To use this, write a normal poll() outer loop, and inside it, after you've handled all the fd's noticed by poll(), you loop callingsigwaitinfo() .

當一個普通的I/O函數如read()write()完成時會發送那個信號。爲了使用這個,在循環外寫一般的poll(),在循環內你處理完全部poll()通知的描述符後你要循環調用sigwaitinfo()
If sigwaitinfo or sigtimedwait returns your realtime signal, siginfo.si_fd and siginfo.si_band give almost the same information as pollfd.fd and pollfd.revents would after a call to poll(), so you handle the i/o, and continue calling sigwaitinfo().

若是sigwaitinfo()sigtimedwait()返回了實時信號,那麼siginfo.si_fd和 siginfo_si_band給出的信息和調用poll()pollfd.fdpollfd.revents的幾乎同樣。若是你處 理該I/O,那麼就繼續調用sigwaitinfo()
If sigwaitinfo returns a traditional SIGIO, the signal queue overflowed, so you flush the signal queue by temporarily changing the signal handler to SIG_DFL , and break back to the outer poll() loop. 

若是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 for example code that uses this feature directly. (Or don't; phhttpd is a bit hard to figure out...)

[Provos, Lever, and Tweedie 2000 ] describes a recent benchmark of phhttpd using a variant of sigtimedwait(), sigtimedwait4(), that lets you retrieve multiple signals with one call. Interestingly, the chief benefit of sigtimedwait4() for them seemed to be it allowed the app to gauge system overload (so it couldbehave appropriately ). (Note that poll() provides the same measure of system overload.)

Signal-per-fd 
Chandra and Mosberger proposed a modification to the realtime signal approach called "signal-per-fd" which reduces or eliminates realtime signal queue overflow by coalescing redundant events. It doesn't outperform epoll, though. Their paper ( www.hpl.hp.com/techreports/2000/HPL-2000-174.html ) compares performance of this scheme with select() and /dev/poll.

Signal-per-fd是由ChandraMosberger提出的對實時信號的一種改進,它能夠減小甚至削除實 時信號的溢出經過oalescing redundant events。然而是它的性能並無epoll論文(www.hpl.hp.com/techreports/2000/HPL-2000-174.html) 比較了它和select()/dev/poll的性能。
Vitaly Luban announced a patch implementing this scheme on 18 May 2001 ; his patch lives atwww.luban.org/GPL/gpl.html . (Note: as of Sept 2001, there may still be stability problems with this patch under heavy load. dkftpbench at about 4500 users may be able to trigger an oops.)

See Poller_sigfd (cc , h ) for an example of how to use signal-per-fd interchangeably with many other readiness notification schemes.

3. Serve many clients with each server thread, and use asynchronous I/O

This has not yet become popular in Unix, probably because few operating systems support asynchronous I/O, also possibly because it (like nonblocking I/O) requires rethinking your application. Under standard Unix, asynchronous I/O is provided by the aio_ interface (scroll down from that link to "Asynchronous input and output"), which associates a signal and value with each I/O operation. Signals and their values are queued and delivered efficiently to the user process. This is from the POSIX 1003.1b realtime extensions, and is also in the Single Unix Specification, version 2.

這個方法在unix上還沒流行,極可能是隻有少部分操做系統支持異步I/O,也亦或是它(例如非阻塞I/O)須要你修改你的應用程序。在標準Unix下,異步I/O特性由aio_接口提供(下拉到連接「異步輸入輸出」),將每一個I/O操做與一個信號和值關聯起來。信號和它們的值排成一隊依序高效的分發至用戶進程中。這種方法屬於POSIX 1003.1b實時標準的擴展,也屬於Single Unix Specification,version 2

AIO is normally used with edge-triggered completion notification, i.e. a signal is queued when the operation is complete. (It can also be used with level triggered completion notification by calling aio_suspend() , though I suspect few people do this.)

AIO一般使用邊緣觸發來完成通知,例如當一個操做完成時將會有一個信號被放入隊列中。(經過調用aio_suspend()AIO也可使用水平觸發來完成通知,儘管我懷疑沒什麼人會這樣作)

glibc 2.1 and later provide a generic implementation written for standards compliance rather than performance.

Glibc 2.1 及其後續版本提供了一個更注重符合標準而不是注重性能的通用實現。

Ben LaHaise's implementation for Linux AIO was merged into the main Linux kernel as of 2.5.32. It doesn't use kernel threads, and has a very efficient underlying api, but (as of 2.6.0-test2) doesn't yet support sockets. (There is also an AIO patch for the 2.4 kernels, but the 2.5/2.6 implementation is somewhat different.) More info:

Ben LaHaise編寫的Linux AIO實現合併到了2.5.32的內核中,它並無採用內核線程,而是使 用了一個高效的underlying api,可是目前它還不支持套接字(2.4內核也有了AIO的補丁,不過 2.5/2.6的實現有必定程序上的不一樣)。

Suparna also suggests having a look at the the DAFS API's approach to AIO .

Suparma建議先看看AIOAPI.

Red Hat AS and Suse SLES both provide a high-performance implementation on the 2.4 kernel; it is related to, but not completely identical to, the 2.6 kernel implementation.

RedHat ASSuse SLES都在2.4的內核中提供了高性能的實現,與2.6的內核實現類似,但並不徹底同樣。

In February 2006, a new attempt is being made to provide network AIO; see the note above about Evgeniy Polyakov's kevent-based AIO .

2006.2,在網絡AIO有了一個新的嘗試,具體請看Evgeniy Polyakov的基於keventAIO.

In 1999, SGI implemented high-speed AIO for Linux . As of version 1.1, it's said to work well with both disk I/O and sockets. It seems to use kernel threads. It is still useful for people who can't wait for Ben's AIO to support sockets.

在1999年,SGIlinux實現了一個高速的AIO。做爲1.1版本,聽說它能在磁盤I/O和套接字上工做得很好。彷佛是它使用了內核線程。目前該實現依然對那些不能等待BenAIO套接字支持的人來講是 頗有用的。 

The O'Reilly book POSIX.4: Programming for the Real World is said to include a good introduction to aio.

O’Reilly 的「POSIX.4: Programming for the Real World」一書對aio作了很好的介紹.

A tutorial for the earlier, nonstandard, aio implementation on Solaris is online at Sunsite . It's probably worth a look, but keep in mind you'll need to mentally convert "aioread" to "aio_read", etc.

這裏 有一個指南介紹了早期的非標準的aio實現,能夠看看,可是請記住你得把」aioread」轉換爲」aio_read」。

Note that AIO doesn't provide a way to open files without blocking for disk I/O; if you care about the sleep caused by opening a disk file, Linus suggests you should simply do the open() in a different thread rather than wishing for an aio_open() system call.

注意AIO並無提供無阻塞的爲磁盤I/O打開文件的方法,若是你在乎因打開磁盤文件而引發 sleep的話,Linus建議 你在另一個線程中調用open()而不是把但願寄託在對aio_open()系統調用上。

Under Windows, asynchronous I/O is associated with the terms "Overlapped I/O" and IOCP or "I/O Completion Port". 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. For more information, see Inside I/O Completion Ports by Mark Russinovich at sysinternals.com, Jeffrey Richter's book "Programming Server-Side Applications for Microsoft Windows 2000" (Amazon , MSPress ), U.S. patent #06223207 , or MSDN .

Windows下,異步I/O與術語」重疊I/O」和」IOCP(I/O Completion Port,I/O完成端口)有必定聯繫。MicrosoftIOCP結合了 先前的如異步I/O(aio_write)的技術,把事件完成的通知進行排隊(就像使用了aio_sigevent字段的aio_write),而且它 爲了保持單一IOCP線程的數量從而阻止了一部分請求。

4. Serve one client with each server thread

... and let read() and write() block. Has the disadvantage of using a whole stack frame for each client, which costs memory. Many OS's also have trouble handling more than a few hundred threads. If each thread gets a 2MB stack (not an uncommon default value), you run out of *virtual memory* at (2^30 / 2^21) = 512 threads on a 32 bit machine with 1GB user-accessible VM (like, say, Linux as normally shipped on x86). You can work around this by giving each thread a smaller stack, but since most thread libraries don't allow growing thread stacks once created, doing this means designing your program to minimize stack use. You can also work around this by moving to a 64 bit processor.

讓read()write()阻塞,這樣很差的地方在於要給每個客戶分配一個完整的棧幀,那太浪費內存了。許多OS在處理上百個線程時會遇到困難。若是每個線程獲得2MB棧空間(這不是一般的默認值),在一臺32位機器上512個線程將消耗1GB的用戶虛擬內存。你能夠爲每個線程分配更少的棧來解決這個問題,可是由於大多數線程庫一旦生成一個線程就不容許其線程棧增加,這意味着你的程序將設計得只能使用最小的棧空間。你也能夠把程序移植到64位處理器上來解決這個問題。

The thread support in Linux, FreeBSD, and Solaris is improving, and 64 bit processors are just around the corner even for mainstream users. Perhaps in the not-too-distant future, those who prefer using one thread per client will be able to use that paradigm even for 10000 clients. Nevertheless, at the current time, if you actually want to support that many clients, you're probably better off using some other paradigm.

LinuxFreeBSDSolaris對線程的支持正變得愈來愈好,64位處理器在不久的未來會愈來愈流行。可能在不久的未來,那些偏心一個線程服務一個客戶模型的人將會使用這樣的範例來支持甚至1000個客戶。然而,若是你如今想支持那麼多的客戶,你最好使用其餘的範例模型。

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 is the name for the standard Linux thread library. It is integrated into glibc since glibc2.0, and is mostly Posix-compliant, but with less than stellar performance and signal support.

LinuxTheads 是標準Linux線程庫的命名。 它從glibc2.0開始已經集成在glibc庫中,而且高度兼容Posix標準,不過在性能和信號的支持度上稍遜一籌。

NGPT: Next Generation Posix Threads for Linux

NGPT is a project started by IBM to bring good Posix-compliant thread support to Linux. It's at stable version 2.2 now, and works well... but the NGPT team has announced that they are putting the NGPT codebase into support-only mode because they feel it's "the best way to support the community for the long term". The NGPT team will continue working to improve Linux thread support, but now focused on improving NPTL. (Kudos to the NGPT team for their good work and the graceful way they conceded to NPTL.)

NGPT是一個由IBM發起的項目,目的是在linux上支持良好的POSIX兼容的線程。NGPT如今是穩定的2.2版本而且工做的很好……可是NGPT團隊已經宣佈他們正在把NGPT代碼庫變爲support-only模式,由於他們以爲這是長期支持社區的最好的方法。NGPT團隊會繼續爲提升linux的線程支持而繼續工做,可是他們如今着眼於的是改進NPTL

NPTL: Native Posix Thread Library for Linux

NPTL is a project by Ulrich Drepper (the benevolent dict^H^H^H^Hmaintainer of glibc ) and Ingo Molnar to bring world-class Posix threading support to Linux.

NPTL是由 Ulrich Drepper ( glibc的主要維護人員)和 Ingo Molnar發起的項目,目的是提供world-classPosix Linux線程支持。

As of 5 October 2003, NPTL is now merged into the glibc cvs tree as an add-on directory (just like linuxthreads), so it will almost certainly be released along with the next release of glibc.

2003.10.5NPTL做爲一個add-on目錄(就像linuxthreads同樣)被合併到glibccvs樹中,因此頗有可能隨glibc的下一次release而 一塊兒發佈。

The first major distribution to include an early snapshot of NPTL was Red Hat 9. (This was a bit inconvenient for some users, but somebody had to break the ice...)

Red Hat 9是最先的包含NPTL的發行版本(對一些用戶來講有點不太方便,可是必須有人來打破這沉默)

NPTL links:

Here's my try at describing the history of NPTL (see also Jerry Cooperstein's article

):

In March 2002, Bill Abt of the NGPT team, the glibc maintainer Ulrich Drepper, and others met to figure out what to do about LinuxThreads. One idea that came out of the meeting was to improve mutex performance; Rusty Russell et al subsequently implemented fast userspace mutexes (futexes) ), which are now used by both NGPT and NPTL. Most of the attendees figured NGPT should be merged into glibc.

在20023月,NGPT團隊的Bill Abtglibc的維護者Ulrich Drepper和其餘人員舉行了一個會面,他們要弄清楚能爲linux線程作些什麼。會議的其中一個想法是提升mutex的性能;Rusty Russell 等人 隨後實現了 fast userspace mutexes (futexes), (現在已在NGPTNPTL中應用了)。 與會的大部分人都認爲NGPT應該合併到glibc中。

Ulrich Drepper, though, didn't like NGPT, and figured he could do better. (For those who have ever tried to contribute a patch to glibc, this may not come as a big surprise :-) Over the next few months, Ulrich Drepper, Ingo Molnar, and others contributed glibc and kernel changes that make up something called the Native Posix Threads Library (NPTL). NPTL uses all the kernel enhancements designed for NGPT, and takes advantage of a few new ones. Ingo Molnar described the kernel enhancements as follows:

Ulrich Drepper儘管不喜歡NGPT,但任然提出他能夠作的更好。(對那些曾經想提供補丁給glibc的人來講,這應該不會令他們感到驚訝)接下來的幾個月,Ulrich Drepper, Ingo Molnar和其餘人發佈了集合了NPTL庫的glibc和內核。NPTL利用了內核爲NGPT的提升所設計的特性,而且利用了一些新的特性。Ingo Molnar對內核的加強特性描述以下:

While NPTL uses the three kernel features introduced by NGPT: getpid() returns PID, CLONE_THREAD and futexes; NPTL also uses (and relies on) a much wider set of new kernel features, developed as part of this project.

儘管NPTL使用了3個由NGPT引進的內核特性:返回進程idgetpid()函數,CLONE_THREADfutexesNPTL也使用了(而且依賴)一個更普遍的做爲這個項目而發展出來的內核新特性。

Some of the items NGPT introduced into the kernel around 2.5.8 got modified, cleaned up and extended, such as thread group handling (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.]

一些由NGPT在內核2.5.8引進的特性獲得了修改,清除和擴展,例如線程組handlingCLONE_THREAD)。

The kernel features developed for and used by NPTL are described in the design whitepaper, http://people.redhat.com/drepper/nptl-design.pdf ...

這些爲NPTL開發的而且後來在NPTL中使用的內核特徵都描述在設計白皮書中。

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.

一個簡易列表:TLS支持,不一樣的clone擴展(CLONE_SETTLS, CLONE_SETTID, CLONE_CLEARTID),POSIX 線程信號處理,sys_exit()擴展(在VM-release基礎上實現了TID futex),sys_exit_group()系統調用,sys_execve()加強和分離線程的支持。

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.

PID的空間也獲得了擴展-例如因爲64k PID assumptions 從而procfs crashed ,max_pid 和 pid 分配的擴展。一些僅在性能提升方面的特性也工做的很好。

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.

本質上,新特性不是對11線程的妥協---內核爲每個能提升線程能力的地方提供幫助,同時咱們爲每個基礎的原始線程精確的作最小的上下文轉換和內核函數調用。

One big difference between the two is that NPTL is a 1:1 threading model, whereas NGPT is an M:N threading model (see below). In spite of this, Ulrich's initial benchmarks seem to show that NPTL is indeed much faster than NGPT. (The NGPT team is looking forward to seeing Ulrich's benchmark code to verify the result.)

NGPTNPTL的一個最大的不一樣就是NPTL1:1的線程模型,而NGPTM:N的編程模型(具體請看下面). 儘管這樣, Ulrich的最初的基準測試 仍是代表NPTLNGPT快不少。

FreeBSD threading support

FreeBSD supports both LinuxThreads and a userspace threading library. Also, a M:N implementation called KSE was introduced in FreeBSD 5.0.

FreeBSD提供了linux線程和一個用戶空間的線程庫。一個叫作KSEM:N的實如今FreeBSD 5.0中被引進。

On 25 Mar 2003, Jeff Roberson posted on freebsd-arch :

... Thanks to the foundation provided by Julian, David Xu, Mini, Dan Eischen, and everyone else who has participated with KSE and libpthread development Mini and I have developed a 1:1 threading implementation. This code works in parallel with KSE and does not break it in any way. It actually helps bring M:N threading closer by testing out shared bits. ...

感謝Julian, David Xu, Mini, Dan Eischen,和其它的每一位參加了KSElibpthread開發的成員所提供的基礎, Mini和我已經開發出了一個11模型的線程實現,它能夠和KSE並行工做而不會帶來任何影響。

And in July 2006, Robert Watson proposed that the 1:1 threading implementation become the default in FreeBsd 7.x :

I know this has been discussed in the past, but I figured with 7.x trundling forward, it was time to think about it again. In benchmarks for many common applications and scenarios, libthr demonstrates significantly better performance over libpthread... libthr is also implemented across a larger number of our platforms, and is already libpthread on several. The first recommendation we make to MySQL and other heavy thread users is "Switch to libthr", which is suggestive, also! ... So the strawman proposal is: make libthr the default threading library on 7.x.

我知道曾經討論過這個問題,可是我認爲隨着7.x的向前推動,這個問題應該從新考慮。 在不少普通的應用程序和特定的基準測試中,libthr明顯的比libpthread在性能上要好得多。 libthr是在咱們大量的平臺上實現的,而libpthread卻只有在幾個平臺上。 最主要的是由於咱們使得Mysql和其它的大量線程的使用者轉換到」libthr」,因此strawman提議:libthr成爲7.x上的默認線程庫。

NetBSD threading support

According to a note from Noriyuki Soda:

Kernel supported M:N thread library based on the Scheduler Activations model is merged into NetBSD-current on Jan 18 2003.

內核支持M:N模型的線程庫,這個庫基於Scheduler Activations模式,在2003118日被合併於NetBSD中。

For details, see An Implementation of Scheduler Activations on the NetBSD Operating System by Nathan J. Williams, Wasabi Systems, Inc., presented at FREENIX '02.

Solaris threading support

The thread support in Solaris is evolving... from Solaris 2 to Solaris 8, the default threading library used an M:N model, but Solaris 9 defaults to 1:1 model thread support.See Sun's multithreaded programming guide andSun's note about Java and Solaris threading

Solaris的線程支持還在進一步提升evolving… 從Solaris 2Solaris 8,默認的線程庫使用的都是M:N模型可是Solaris 9卻默認使用了1:1線程模型查看Sun多線程編程指南 和Sun的關於JavaSolaris線程的note.

Java threading support in JDK 1.3.x and earlier

As is well known, Java up to JDK1.3.x did not support any method of handling network connections other than one thread per client. Volanomark is a good microbenchmark which measures throughput in messsages per second at various numbers of simultaneous connections. As of May 2003, JDK 1.3 implementations from various vendors are in fact able to handle ten thousand simultaneous connections -- albeit with significant performance degradation. See Table 4 for an idea of which JVMs can handle 10000 connections, and how performance suffers as the number of connections increases.

你們都知道,Java一直到JDK1.3.x都沒有支持任何處理網絡鏈接的方法,除了一個線程服務一個客戶端的模型以外。 Volanomark是一個不錯的微型測試程序,能夠用來測量在 某個時候不一樣數目的網絡鏈接時每秒鐘的信息吞吐量。在2003.5, JDK 1.3的實現實際上能夠同時處理10000個鏈接,可是性能卻嚴重降低了。 從Table 4 能夠看出JVMs能夠處理10000個鏈接,可是隨着鏈接數目的增加性能也逐步降低。

Note: 1:1 threading vs. M:N threading

There is a choice when implementing a threading library: you can either put all the threading support in the kernel (this is called the 1:1 threading model), or you can move a fair bit of it into userspace (this is called the M:N threading model). At one point, M:N was thought to be higher performance, but it's so complex that it's hard to get right, and most people are moving away from it.

在實現線程庫的時候有一個選擇就是你能夠把全部的線程支持都放到內核中(也就是所謂的11的模型),也能夠 把一些線程移到用戶空間上去(也就是所謂的MN模型)。從某個角度來講, M:N被認爲擁有更好的性能,可是因爲很難被正確的編寫, 因此大部分人都遠離了該方法。

5. Build the server code into the kernel

Novell and Microsoft are both said to have done this at various times, at least one NFS implementation does this, khttpd does this for Linux and static web pages, and "TUX" (Threaded linUX webserver) is a blindingly fast and flexible kernel-space HTTP server by Ingo Molnar for Linux. Ingo's September 1, 2000 announcement says an alpha version of TUX can be downloaded from ftp://ftp.redhat.com/pub/redhat/tux , and explains how to join a mailing list for more info. 

聽說NovellMicrosoft在不一樣的時間完成了這個,至少一個NFS實現作到了這一點,khttpdlinux上實現了靜態網頁web服務器,Ingo Molnar完成了「TUX」 (Threaded linUX webserver) ,這是一個Linux下的快速的可擴展的內核空間的HTTP服務器。 Ingo2000.9.1宣佈 alpha版本的TUX能夠在 ftp://ftp.redhat.com/pub/redhat/tux下載而且介紹瞭如何加入其郵件列表來獲取更多信息。
The linux-kernel list has been discussing the pros and cons of this approach, and the consensus seems to be instead of moving web servers into the kernel, the kernel should have the smallest possible hooks added to improve web server performance. That way, other kinds of servers can benefit. See e.g. Zach Brown's remarksabout userland vs. kernel http servers. It appears that the 2.4 linux kernel provides sufficient power to user programs, as the X15 server runs about as fast as Tux, but doesn't use any kernel modifications.

Linux內核的郵件列表上討論了該方法的好處和缺點,多數人認爲不該該把web服務器放進內核中, 相反內核加入最小的鉤子hooks來提升web服務器的性能,這樣對其它形式的服務器就有益。 具體請看 Zach Brown的討論 對比用戶級別和內核的http服務器。 在2.4linux內核中爲用戶程序提供了足夠的權力(power),就像X15 服務器運行的速度和TUX幾乎同樣,可是它沒有對內核作任何改變。

 

Comments

Richard Gooch has written a paper discussing I/O options .

Richard Gooch寫了一片文章來討論I/O選項

In 2001, Tim Brecht and MMichal Ostrowski measured various strategies for simple select-based servers. Their data is worth a look.

在2001年,Tim BrechtMMichal Ostrowski 測試了基於簡單的select服務器的幾種實現策略。這些數據任然值得一看。

In 2003, Tim Brecht posted source code for userver , a small web server put together from several servers written by Abhishek Chandra, David Mosberger, David Pariag, and Michal Ostrowski. It can use select(), poll(), epoll(), or sigio.

在2003年,Tim Brecht 發佈了userver的源代碼,userver是一個集合了由Abhishek Chandra, David Mosberger, David Pariag, and Michal Ostrowski編寫的幾種服務器的小的web服務器。Userver能使用select(),poll(),epoll()sigio

Back in March 1999, Dean Gaudet posted :

I keep getting asked "why don't you guys use a select/event based model like Zeus? It's clearly the fastest." ...

在19993月,Dean Gaudet說:我總是被問到爲何大家不像Zeus那樣使用基於select/event的模型?那明顯是最快的。......

His reasons boiled down to "it's really hard, and the payoff isn't clear". Within a few months, though, it became clear that people were willing to work on it.

他把理由概括爲 :這的確很難,而且這樣的回報並不明確。儘管還不到幾個月,人們愈來愈願意採用那樣的方法。

Mark Russinovich wrote an editorial and an article discussing I/O strategy issues in the 2.2 Linux kernel. Worth reading, even he seems misinformed on some points. In particular, he seems to think that Linux 2.2's asynchronous I/O (see F_SETSIG above) doesn't notify the user process when data is ready, only when new connections arrive. This seems like a bizarre misunderstanding. See also comments on an earlier draft , Ingo Molnar's rebuttal of 30 April 1999 , Russinovich's comments of 2 May 1999 , a rebuttal from Alan Cox, and various posts to linux-kernel . I suspect he was trying to say that Linux doesn't support asynchronous disk I/O, which used to be true, but now that SGI has implemented KAIO , it's not so true anymore.

Mark Russinovich 寫了一篇評論和文章來討論linux 2.2 內核中的I/O策略。儘管他好像在一些方面有了一些誤解,但這任然值得一讀。特別的,他認爲linux 2.2 的異步I/O在數據準備好的時候不會通知用戶進程,除非是一個新的鏈接到達了。這看起來是一個巨大的誤解。再看看comments on an earlier draft , Ingo Molnar's rebuttal of 30 April 1999 , Russinovich's comments of 2 May 1999 , a rebuttal from Alan Cox, and various posts to linux-kernel。我懷疑他想說linux不支持磁盤異步I/O,那在之前是對的,但如今SGI已經實現了KAIO,所以這樣的說法將會是再也不正確的了。

See these pages at sysinternals.com and MSDN for information on "completion ports", which he said were unique to NT; in a nutshell, win32's "overlapped I/O" turned out to be too low level to be convenient, and a "completion port" is a wrapper that provides a queue of completion events, plus 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).

看看sysinternals.com and MSDN上關於完成端口的相關頁面,他說的僅僅是對NT的。簡單的說,win32overlapped I/O被證實過低級了以致於很不方便使用,可是完成端口是一個完成時間隊列的包裝,再加上魔法般的調度, 經過容許更多的線程來得到完成事件若是該端口上的其它已得到完成事件的線程處於睡眠中時(可能正在處理阻塞I/O),從而能夠保持運行線程數目恆定

See also OS/400's support for I/O completion ports .

There was an interesting discussion on linux-kernel in September 1999 titled "> 15,000 Simultaneous Connections " (and the second week of the thread). Highlights:

1999.9,在linux內核郵件列表上曾有一次很是有趣的討論,討論題目爲 "15,000 Simultaneous Connections" (而且延續到第二週)

  • Ed Hall posted a few notes on his experiences; he's achieved >1000 connects/second on a UP P2/333 running Solaris. His code used a small pool of threads (1 or 2 per CPU) each managing a large number of clients using "an event-based model".
  • Mike Jagdis posted an analysis of poll/select overhead , and said "The current select/poll implementation can be improved significantly, especially in the blocking case, but the overhead will still increase with the number of descriptors because select/poll does not, and cannot, remember what descriptors are interesting. This would be easy to fix with a new API. Suggestions are welcome..."
  • Mike posted about his work on improving select() and poll() .
  • Mike posted a bit about a possible API to replace poll()/select() : "How about a 'device like' API where you write 'pollfd like' structs, the 'device' listens for events and delivers 'pollfd like' structs representing them when you read it? ... "
  • Rogier Wolff suggested using "the API that the digital guys suggested",http://www.cs.rice.edu/~gaurav/papers/usenix99.ps
  • Joerg Pommnitz pointed out that any new API along these lines should be able to wait for not just file descriptor events, but also signals and maybe SYSV-IPC. Our synchronization primitives should certainly be able to do what Win32's WaitForMultipleObjects can, at least.
  • Stephen Tweedie asserted that the combination of F_SETSIG, queued realtime signals, and sigwaitinfo() was a superset of the API proposed in http://www.cs.rice.edu/~gaurav/papers/usenix99.ps. He also mentions that you keep the signal blocked at all times if you're interested in performance; instead of the signal being delivered asynchronously, the process grabs the next one from the queue with sigwaitinfo().
  • Jayson Nordwick compared completion ports with the F_SETSIG synchronous event model, and concluded they're pretty similar.
  • Alan Cox noted that an older rev of SCT's SIGIO patch is included in 2.3.18ac.
  • Jordan Mendelson posted some example code showing how to use F_SETSIG.
  • Stephen C. Tweedie continued the comparison of completion ports and F_SETSIG, and noted: "With a signal dequeuing mechanism, your application is going to get signals destined for various library components if libraries are using the same mechanism," but the library can set up its own signal handler, so this shouldn't affect the program (much).
  • Doug Royer noted that he'd gotten 100,000 connections on Solaris 2.6 while he was working on the Sun calendar server. Others chimed in with estimates of how much RAM that would require on Linux, and what bottlenecks would be hit.

Interesting reading!

 

Limits on open filehandles

  • Any Unix: the limits set by ulimit or setrlimit.
  • Solaris: see the Solaris FAQ, question 3.46 (or thereabouts; they renumber the questions periodically).
  • FreeBSD:

    Edit /boot/loader.conf, add the line 

set kern.maxfiles=XXXX

 

  • where 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. 
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."

  • Another reader says 

"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: 

the FreeBSD handbook

SYSCTL TUNING , LOADER TUNABLES , and KERNEL CONFIG TUNING in 'man tuning'

The Effects of Tuning a FreeBSD 4.3 Box for High Performance , Daemon News, Aug 2001

postfix.org tuning notes , covering FreeBSD 4.2 and 4.4

the Measurement Factory's notes , circa FreeBSD 4.3

  • OpenBSD: A reader says 

"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-max

  • increases the system limit on open files, and 

ulimit -n 32768

 

  • increases the current process' limit. 

On 2.2.x kernels,

echo 32768 > /proc/sys/fs/file-max
echo 65536 > /proc/sys/fs/inode-max

  • increases the system limit on open files, and 

ulimit -n 32768

 

  • increases 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.

Limits on threads

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.

  • Solaris: it supports as many threads as will fit in memory, I hear.
  • Linux 2.6 kernels with NPTL: /proc/sys/vm/max_map_count may need to be increased to go above 32000 or so threads. (You'll need to use very small stack threads to get anywhere near that number of threads, though, unless you're on a 64 bit processor.) See the NPTL mailing list, e.g. the thread with subject "Cannot create more than 32K threads? ", for more info.
  • Linux 2.4: /proc/sys/kernel/threads-max is the max number of threads; it defaults to 2047 on my Red Hat 8 system. You can set increase this as usual by echoing new values into that file, e.g. "echo 4000 > /proc/sys/kernel/threads-max"
  • Linux 2.2: Even the 2.2.13 kernel limits the number of threads, at least on Intel. I don't know what the limits are on other architectures. Mingo posted a patch for 2.1.131 on Intel that removed this limit. It appears to be integrated into 2.3.20. 

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.

Java issues

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 connections are 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:

  • Matt Welsh's Jaguar system proposes preserialized objects, new Java bytecodes, and memory management changes to allow the use of asynchronous I/O with Java.
  • Interfacing Java to the Virtual Interface Architecture , by C-C. Chang and T. von Eicken, proposes memory management changes to allow the use of asynchronous I/O with Java.
  • JSR-51 was the Sun project that came up with the java.nio package. Matt Welsh participated (who says Sun doesn't listen?).

Other tips

  • Zero-Copy 
    Normally, data gets copied many times on its way from here to there. Any scheme that eliminates these copies to the bare physical minimum is called "zero-copy". 

Thomas Ogrisegg's zero-copy send patch for mmaped files under Linux 2.4.17-2.4.20. Claims it's faster than sendfile().

IO-Lite is a proposal for a set of I/O primitives that gets rid of the need for many copies.

Alan Cox noted that zero-copy is sometimes not worth the trouble back in 1999. (He did like sendfile(), though.)

o Ingo implemented a form of zero-copy TCP in the 2.4 kernel for TUX 1.0 in July 2000, and says he'll make it available to userspace soon.

Drew Gallatin and Robert Picco have added some zero-copy features to FreeBSD ; the idea seems to be that if you call write() or read() on a socket, the pointer is page-aligned, and the amount of data transferred is at least a page, *and* you don't immediately reuse the buffer, memory management tricks will be used to avoid copies. But see followups to this message on linux-kernel for people's misgivings about the speed of those memory management tricks. 

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.

o The sendfile() system call can implement zero-copy networking. 
The sendfile() function in Linux and FreeBSD lets you tell the kernel to send part or all of a file. This lets the OS do it as efficiently as possible. It can be used equally well in servers using threads or servers using nonblocking I/O. (In Linux, it's poorly documented at the moment; use _syscall4 to call it . Andi Kleen is writing new man pages that cover this. See also Exploring The sendfile System Call by Jeff Tranter in Linux Gazette issue 91.) Rumor has it , ftp.cdrom.com benefitted noticeably from sendfile(). 

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.

  • Avoid small frames by using writev (or TCP_CORK) 
    A new socket option under Linux, TCP_CORK, tells the kernel to avoid sending partial frames, which helps a bit e.g. when there are lots of little write() calls you can't bundle together for some reason. Unsetting the option flushes the buffer. Better to use writev(), though... 

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.

  • Behave sensibly on overload. 
    [Provos, Lever, and Tweedie 2000 ] notes that dropping incoming connections when the server is overloaded improved the shape of the performance curve, and reduced the overall error rate. They used a smoothed version of "number of clients with I/O ready" as a measure of overload. This technique should be easily applicable to servers written with select, poll, or any system call that returns a count of readiness events per call (e.g. /dev/poll or sigtimedwait4()).
  • Some programs can benefit from using non-Posix threads. 
    Not all threads are created equal. The clone() function in Linux (and its friends in other operating systems) lets you create a thread that has its own current working directory, for instance, which can be very helpful when implementing an ftp server. See Hoser FTPd for an example of the use of native threads rather than pthreads.
  • Caching your own data can sometimes be a win. 
    "Re: fix for hybrid server problems" by Vivek Sadananda Pai (vivek@cs.rice.edu) on new-httpd , May 9th, states: 

"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/ "

Other limits

  • Old system libraries might use 16 bit variables to hold file handles, which causes trouble above 32767 handles. glibc2.1 should be ok.
  • Many systems use 16 bit variables to hold process or thread id's. It would be interesting to port theVolano scalability benchmark to C, and see what the upper limit on number of threads is for the various operating systems.
  • Too much thread-local memory is preallocated by some operating systems; if each thread gets 1MB, and total VM space is 2GB, that creates an upper limit of 2000 threads.
  • Look at the performance comparison graph at the bottom ofhttp://www.acme.com/software/thttpd/benchmarks.html . Notice how various servers have trouble above 128 connections, even on Solaris 2.6? Anyone who figures out why, let me know. 
    Note: if the TCP stack has a bug that causes a short (200ms) delay at SYN or FIN time, as Linux 2.2.0-2.2.6 had, and the OS or http daemon has a hard limit on the number of connections open, you would expect exactly this behavior. There may be other causes.

Kernel Issues

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.

Mohit Aron (aron@cs.rice.edu) writes that rate-based clocking in TCP can improve HTTP response time over 'slow' connections by 80%.

Measuring Server Performance

Two tests in particular are simple, interesting, and hard:

7 raw connections per second (how many 512 byte files per second can you serve?)

8 total transfer rate on large files with many slow clients (how many 28.8k modem clients can simultaneously download from your server before performance goes to pot?)

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.

Examples

Interesting select()-based servers

Interesting /dev/poll-based servers

  • N. Provos, C. Lever , "Scalable Network I/O in Linux," May, 2000. [FREENIX track, Proc. USENIX 2000, San Diego, California (June, 2000).] Describes a version of thttpd modified to support /dev/poll. Performance is compared with phhttpd.

Interesting kqueue()-based servers

Interesting realtime signal-based servers

  • Chromium's X15. This uses the 2.4 kernel's SIGIO feature together with sendfile() and TCP_CORK, and reportedly achieves higher speed than even TUX. The source is available under a community source (not open source) license. See the original announcement by Fabio Riccardi.
  • Zach Brown's phhttpd - "a quick web server that was written to showcase the sigio/siginfo event model. consider this code highly experimental and yourself highly mental if you try and use it in a production environment." Uses the siginfo features of 2.3.21 or later, and includes the needed patches for earlier kernels. Rumored to be even faster than khttpd. See his post of 31 May 1999 for some notes.

Interesting thread-based servers

Interesting in-kernel servers

Other interesting links

相關文章
相關標籤/搜索