青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

網絡服務器軟件開發/中間件開發,關注ACE/ICE/boost

C++博客 首頁 新隨筆 聯系 聚合 管理
  152 Posts :: 3 Stories :: 172 Comments :: 0 Trackbacks
摘要


編寫連接數巨大的高負載服務器程序時,經典的多線程模式和select模式都不再適用。
應當拋棄它們,采用epoll/kqueue/dev_poll來捕獲I/O事件。最后簡要介紹了AIO。


由來


網絡服務在處理數以萬計的客戶端連接時,往往出現效率低下甚至完全癱瘓,這被稱為
C10K問題。隨著互聯網的迅速發展,越來越多的網絡服務開始面臨C10K問題,作為大型
網站的開發人員有必要對C10K問題有一定的了解。本文的主要參考文獻是
<http://www.kegel.com/c10k.htmlhttp://www.kegel.com/c10k.htmls

C10K問題的最大特點是:設計不夠良好的程序,其性能和連接數及機器性能的關系往往
是非線性的。舉個例子:如果沒有考慮過C10K問題,一個經典的基于select的程序能在
舊服務器上很好處理1000并發的吞吐量,它在2倍性能新服務器上往往處理不了并發
2000的吞吐量。

這是因為在策略不當時,大量操作的消耗和當前連接數n成線性相關。會導致單個任務
的資源消耗和當前連接數的關系會是O(n)。而服務程序需要同時對數以萬計的socket進
行I/O處理,積累下來的資源消耗會相當可觀,這顯然會導致系統吞吐量不能和機器性
能匹配。為解決這個問題,必須改變對連接提供服務的策略。


基本策略


主要有兩方面的策略:1.應用軟件以何種方式和操作系統合作,獲取I/O事件并調度多
個socket上的I/O操作;2. 應用軟件以何種方式處理任務和線程/進程的關系。前者主
要有阻塞I/O、非阻塞I/O、異步I/O這3種方案,后者主要有每任務1進程、每任務1線
程、單線程、多任務共享線程池以及一些更復雜的變種方案。常用的經典策略如下:

1.         Serve one client with each thread/process, and use blocking I/O
這是小程序和java常用的策略,對于交互式的長連接應用也是常見的選擇(比如BBS)。
這種策略很能難足高性能程序的需求,好處是實現極其簡單,容易嵌入復雜的交互邏
輯。Apache、ftpd等都是這種工作模式。

2.         Serve many clients with single thread, and use nonblocking I/O
and readiness notification
這是經典模型,datapipe等程序都是如此實現的。優點在于實現較簡單,方便移植,也
能提供足夠的性能;缺點在于無法充分利用多CPU的機器。尤其是程序本身沒有復雜的
業務邏輯時。

3.         Serve many clients with each thread, and use nonblocking I/O and
readiness notification
對經典模型2的簡單改進,缺點是容易在多線程并發上出bug,甚至某些OS不支持多線程
操作readiness notification。

4.         Serve many clients with each thread, and use asynchronous I/O
在有AI/O支持的OS上,能提供相當高的性能。不過AI/O編程模型和經典模型差別相當
大,基本上很難寫出一個框架同時支持AI/O和經典模型,降低了程序的可移植性。在
Windows上,這基本上是唯一的可選方案。

本文主要討論模型2的細節,也就是在模型2下應用軟件如何處理Socket I/O。


select 與 poll


最原始的同步阻塞 I/O 模型的典型流程如下:

同步阻塞 I/O 模型的典型流程

從應用程序的角度來說,read 調用會延續很長時間,應用程序需要相當多線程來解決
并發訪問問題。同步非阻塞I/O對此有所改進:

經典的單線程服務器程序結構往往如下:


do {

         Get Readiness Notification of all sockets

         Dispatch ready handles to corresponding handlers

                   If (readable) {
                            read the socket

                            If (read done)

                                     Handler process the request
                   }

                   if (writable)

                            write response

                   if (nothing to do)

                            close socket

} while(True)

非阻塞 I/O 模型的典型流程:

異步阻塞 I/O 模型的典型流程

其中關鍵的部分是readiness notification,找出哪一個socket上面發生了I/O事件。
一般從教科書和例子程序中首先學到的是用select來實現。Select定義如下:


int select(int n, fd_set *rd_fds, fd_set *wr_fds, fd_set *ex_fds, struct
timeval *timeout);

Select用到了fd_set結構,從man page里可以知道fd_set能容納的句柄和FD_SETSIZE相
關。實際上fd_set在*nix下是一個bit標志數組,每個bit表示對應下標的fd是不是在
fd_set中。fd_set只能容納編號小于 FD_SETSIZE的那些句柄。

FD_SETSIZE默認是1024,如果向fd_set里放入過大的句柄,數組越界以后程序就會垮
掉。系統默認限制了一個進程最大的句柄號不超過1024,但是可以通過ulimit -n命令
/setrlimit函數來擴大這一限制。如果不幸一個程序在FD_SETSIZE=1024的環境下編
譯,運行時又遇到ulimit –n > 1024的,那就只有祈求上帝保佑不會垮掉了。


在ACE環境中,ACE_Select_Reactor針對這一點特別作了保護措施,但是還是有recv_n
這樣的函數間接的使用了select,這需要大家注意。

針對fd_set的問題,*nix提供了poll函數作為select的一個替代品。Poll的接口如下:


int poll(struct pollfd *ufds, unsigned int nfds, int timeout);

第1個參數ufds是用戶提供的一個pollfd數組,數組大小由用戶自行決定,因此避免了
FD_SETSIZE帶來的麻煩。Ufds是fd_set的一個完全替代品,從select到poll的移植很方
便。到此為止,至少我們面對C10K,可以寫出一個能work的程序了。

然而Select和Poll在連接數增加時,性能急劇下降。這有兩方面的原因:首先操作系統
面對每次的select/poll操作,都需要重新建立一個當前線程的關心事件列表,并把線
程掛在這個復雜的等待隊列上,這是相當耗時的。其次,應用軟件在select/poll返回
后也需要對傳入的句柄列表做一次掃描來dispatch,這也是很耗時的。這兩件事都是和
并發數相關,而I/O事件的密度也和并發數相關,導致CPU占用率和并發數近似成O(n2)
的關系。


epoll, kqueue, /dev/poll


因為以上的原因,*nix的hacker們開發了epoll, kqueue, /dev/poll這3套利器來幫助
大家,讓我們跪拜三分鐘來感謝這些大神。其中epoll是linux的方案,kqueue是
freebsd的方案,/dev/poll是最古老的Solaris的方案,使用難度依次遞增。

簡單的說,這些api做了兩件事:1.避免了每次調用select/poll時kernel分析參數建立
事件等待結構的開銷,kernel維護一個長期的事件關注列表,應用程序通過句柄修改這
個列表和捕獲I/O事件。2.避免了select/poll返回后,應用程序掃描整個句柄表的開
銷,Kernel直接返回具體的事件列表給應用程序。

在接觸具體api之前,先了解一下邊緣觸發(edge trigger)和條件觸發(level trigger)
的概念。邊緣觸發是指每當狀態變化時發生一個io事件,條件觸發是只要滿足條件就發
生一個io事件。舉個讀socket的例子,假定經過長時間的沉默后,現在來了100個字
節,這時無論邊緣觸發和條件觸發都會產生一個read ready notification通知應用程
序可讀。應用程序讀了50個字節,然后重新調用api等待io事件。這時條件觸發的api會
因為還有50個字節可讀從而立即返回用戶一個read ready notification。而邊緣觸發
的api會因為可讀這個狀態沒有發生變化而陷入長期等待。

因此在使用邊緣觸發的api時,要注意每次都要讀到socket返回EWOULDBLOCK為止,否則
這個socket就算廢了。而使用條件觸發的api時,如果應用程序不需要寫就不要關注
socket可寫的事件,否則就會無限次的立即返回一個write ready notification。大家
常用的select就是屬于條件觸發這一類,以前本人就犯過長期關注socket寫事件從而
CPU 100%的毛病。

epoll的相關調用如下:


int epoll_create(int size)


int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)


int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int
timeout)

epoll_create創建kernel中的關注事件表,相當于創建fd_set。

epoll_ctl修改這個表,相當于FD_SET等操作

epoll_wait等待I/O事件發生,相當于select/poll函數

epoll完全是select/poll的升級版,支持的事件完全一致。并且epoll同時支持邊緣觸
發和條件觸發,一般來講邊緣觸發的性能要好一些。這里有個簡單的例子:


struct epoll_event ev, *events;

int kdpfd = epoll_create(100);

ev.events = EPOLLIN | EPOLLET;  // 注意這個EPOLLET,指定了邊緣觸發

ev.data.fd =listener;

epoll_ctl(kdpfd, EPOLL_CTL_ADD, listener, &ev);

for(;;) {

   nfds = epoll_wait(kdpfd, events, maxevents, -1);

 

   for(n = 0; n < nfds; ++n) {

       if(events[n].data.fd == listener) {

           client = accept(listener, (struct sockaddr *) &local,

                           &addrlen);

           if(client < 0){

               perror("accept");

               continue;

           }

           setnonblocking(client);

           ev.events = EPOLLIN | EPOLLET;

           ev.data.fd = client;

           if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, client, &ev) < 0) {

               fprintf(stderr, "epoll set insertion error: fd=%d0,

                       client);

               return -1;

           }

       }

       else

           do_use_fd(events[n].data.fd);

   }

}

簡單介紹一下kqueue和/dev/poll

kqueue是freebsd的寵兒,kqueue實際上是一個功能相當豐富的kernel事件隊列,它不
僅僅是select/poll的升級,而且可以處理signal、目錄結構變化、進程等多種事件。
Kqueue是邊緣觸發的

/dev/poll是Solaris的產物,是這一系列高性能API中最早出現的。Kernel提供一個特
殊的設備文件/dev/poll。應用程序打開這個文件得到操縱fd_set的句柄,通過寫入
pollfd來修改它,一個特殊ioctl調用用來替換select。由于出現的年代比較早,所以
/dev/poll的接口現在看上去比較笨拙可笑。

C++開發:ACE 5.5以上版本提供了ACE_Dev_Poll_Reactor封裝了epoll和/dev/poll兩種
api,需要分別在config.h中定義ACE_HAS_EPOLL和ACE_HAS_DEV_POLL來啟用。

Java開發: JDK 1.6的Selector提供了對epoll的支持,JDK1.4提供了對/dev/poll的支
持。只要選擇足夠高的JDK版本就行了。


異步I/O以及Windows


和經典模型不同,異步I/O提供了另一種思路。和傳統的同步I/O不同,異步I/O允許進
程發起很多 I/O 操作,而不用阻塞或等待任何操作完成。稍后或在接收到 I/O 操作完
成的通知時,進程就可以檢索 I/O 操作的結果。

異步非阻塞 I/O 模型是一種處理與 I/O 重疊進行的模型。讀請求會立即返回,說明
read 請求已經成功發起了。在后臺完成讀操作時,應用程序然后會執行其他處理操
作。當 read 的響應到達時,就會產生一個信號或執行一個基于線程的回調函數來完成
這次 I/O 處理過程。異步I/O 模型的典型流程:

異步非阻塞 I/O 模型的典型流程

對于文件操作而言,AIO有一個附帶的好處:應用程序將多個細碎的磁盤請求并發的提
交給操作系統后,操作系統有機會對這些請求進行合并和重新排序,這對同步調用而言
是不可能的——除非創建和請求數目同樣多的線程。

Linux Kernel 2.6提供了對AIO的有限支持——僅支持文件系統。libc也許能通過來線
程來模擬socket的AIO,不過這對性能沒意義。總的來說Linux的aio還不成熟

Windows對AIO的支持很好,有IOCP隊列和IPCP回調兩種方式,甚至提供了用戶級異步調
用APC功能。Windows下AIO是唯一可用的高性能方案,詳情請參考MSDN 

Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1537545


posted on 2007-04-05 09:00 true 閱讀(1255) 評論(0)  編輯 收藏 引用 所屬分類: ACE
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            亚洲精品一区二区三区av| 久久综合伊人77777麻豆| 亚洲欧美日韩区| 狂野欧美性猛交xxxx巴西| 免费成人av在线看| 欧美va亚洲va日韩∨a综合色| 久久男女视频| 亚洲电影免费| 亚洲激情在线播放| 亚洲婷婷综合久久一本伊一区| 亚洲一区日韩在线| 久久精品观看| 欧美日韩三区四区| 国产欧美日韩视频一区二区| 在线观看日韩| 亚洲视频在线观看| 久久久久久一区二区三区| 欧美激情在线观看| 一区二区电影免费在线观看| 久久激情视频| 欧美日韩视频不卡| 在线日韩av永久免费观看| 亚洲桃色在线一区| 蜜桃av噜噜一区| 一区二区免费在线观看| 久久久国产精彩视频美女艺术照福利| 久久亚洲国产成人| 国产精品美女久久久免费| 亚洲国产精品一区二区第一页| 亚洲免费观看| 可以免费看不卡的av网站| 99热这里只有成人精品国产| 久久av一区二区三区| 欧美日韩国产麻豆| 在线观看欧美日韩国产| 亚洲欧美亚洲| 亚洲人成7777| 免费观看成人网| 国产乱子伦一区二区三区国色天香| 亚洲国产成人91精品| 羞羞色国产精品| 91久久精品视频| 男同欧美伦乱| 在线观看视频欧美| 欧美中文字幕第一页| 亚洲最新在线视频| 欧美日韩岛国| 亚洲精品免费在线观看| 噜噜噜久久亚洲精品国产品小说| 亚洲欧美激情在线视频| 欧美日韩免费区域视频在线观看| 亚洲激情二区| 欧美国产视频一区二区| 久久精品视频免费观看| 国产色爱av资源综合区| 欧美有码在线视频| 午夜精品理论片| 国产欧美成人| 久久女同互慰一区二区三区| 性8sex亚洲区入口| 国产午夜亚洲精品理论片色戒| 欧美一级网站| 亚洲一区二区三区四区中文| 欧美不卡三区| 久久综合伊人77777麻豆| 极品av少妇一区二区| 久久se精品一区二区| 午夜精品久久久久久久白皮肤| 国产精品久久久久久久久久久久久久| 99国产精品视频免费观看| 亚洲人成网站在线观看播放| 欧美黄色片免费观看| 日韩一级欧洲| 亚洲精品一区二区三区婷婷月 | 99精品免费视频| 欧美日韩在线不卡一区| 亚洲一区二区成人| 亚洲欧美一区二区精品久久久| 国产精品美女视频网站| 欧美影院在线播放| 久久免费的精品国产v∧| 日韩视频第一页| 亚洲视频你懂的| 激情欧美一区二区三区| 91久久亚洲| 国产精品呻吟| 欧美高清在线精品一区| 欧美日本国产在线| 香港久久久电影| 久久婷婷av| 亚洲伊人第一页| 欧美在线观看网站| 一区二区欧美在线观看| 欧美亚洲免费高清在线观看| 1024成人| 亚洲欧美激情诱惑| 亚洲人成高清| 欧美一区二粉嫩精品国产一线天| 亚洲精品黄网在线观看| 午夜精品剧场| 亚洲午夜精品| 欧美电影在线观看| 欧美影院在线| 欧美日韩综合不卡| 欧美电影在线免费观看网站| 国产精品任我爽爆在线播放| 久久久久久噜噜噜久久久精品| 欧美激情视频在线免费观看 欧美视频免费一 | 欧美国产精品人人做人人爱| 性伦欧美刺激片在线观看| 久久综合久久综合九色| 亚洲欧美www| 欧美高清视频一二三区| 久久精品天堂| 欧美日韩国产高清| 亚洲成在线观看| 狠狠久久综合婷婷不卡| 亚洲自拍三区| 亚洲性视频网址| 午夜精品免费视频| 亚洲一区欧美一区| 亚洲天堂免费观看| 欧美成人资源| 影音先锋另类| 欧美激情久久久久| 国产在线观看一区| 日韩图片一区| 亚洲国产成人精品久久久国产成人一区| 久久综合狠狠综合久久激情| 久久夜色精品一区| 久久久久**毛片大全| 国产精品试看| 亚洲午夜激情| 亚洲综合精品四区| 欧美日韩在线亚洲一区蜜芽| 亚洲高清不卡在线| 亚洲欧洲精品成人久久奇米网| 欧美在线地址| 久久一二三国产| 在线观看国产一区二区| 久久香蕉国产线看观看av| 免费欧美日韩| 亚洲精品久久久久久久久久久久久 | 蜜桃久久av| 亚洲国产成人高清精品| 亚洲破处大片| 欧美日韩国产免费| 亚洲香蕉在线观看| 欧美综合激情网| 在线观看成人网| 欧美日韩黄色大片| 亚洲综合二区| 免费成人性网站| 亚洲少妇最新在线视频| 国产精品美女久久久| 午夜在线精品偷拍| 欧美成人免费va影院高清| 99国产精品国产精品毛片| 欧美日韩在线看| 午夜精品亚洲一区二区三区嫩草| 久久成人免费视频| 亚洲第一中文字幕在线观看| 久久影音先锋| 亚洲伦理在线| 久久久亚洲国产天美传媒修理工| 亚洲国产精品久久久久秋霞蜜臀 | 99在线精品观看| 亚洲欧美国产高清| 激情校园亚洲| 欧美日韩亚洲综合一区| 欧美一区三区二区在线观看| 欧美成人免费一级人片100| 亚洲性av在线| 在线观看成人一级片| 欧美午夜片在线观看| 久久av免费一区| 99国产麻豆精品| 久久综合久久久久88| 99在线|亚洲一区二区| 国产欧美日韩一区二区三区在线观看 | 久久久久久久综合日本| 亚洲清纯自拍| 久久精品夜色噜噜亚洲a∨| 9l国产精品久久久久麻豆| 国产亚洲欧美一区| 欧美日韩一区二区欧美激情| 久久国产精品久久久| 亚洲蜜桃精久久久久久久 | 亚洲精品女av网站| 性欧美暴力猛交另类hd| 亚洲精品视频在线观看网站| 国产亚洲激情在线| 欧美日韩一区二区精品| 免费视频亚洲| 久久久国产精品亚洲一区| 亚洲视频免费在线观看| 日韩性生活视频| 亚洲日本成人女熟在线观看| 美女国产精品| 久热精品在线视频|