日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

Epoll的ET模式與LT模式

 mrjbydd 2011-10-25

Epoll的ET模式與LT模式   

2008-07-09 14:35:30|  分類: 其它 |  標(biāo)簽: |字號 訂閱

ET(Edge Triggered)與LT(Level Triggered)的主要區(qū)別可以從下面的例子看出
eg:
1. 標(biāo)示管道讀者的文件句柄注冊到epoll中;
2. 管道寫者向管道中寫入2KB的數(shù)據(jù);
3. 調(diào)用epoll_wait可以獲得管道讀者為已就緒的文件句柄;
4. 管道讀者讀取1KB的數(shù)據(jù)
5. 一次epoll_wait調(diào)用完成
如果是ET模式,管道中剩余的1KB被掛起,再次調(diào)用epoll_wait,得不到管道讀者的文件句柄,除非有新的數(shù)據(jù)寫入管道。如果是LT模式,只要管道中有數(shù)據(jù)可讀,每次調(diào)用epoll_wait都會觸發(fā)。

另一點區(qū)別就是設(shè)為ET模式的文件句柄必須是非阻塞的。
三、 Epoll的實現(xiàn)
Epoll的源文件在/usr/src/linux/fs/eventpoll.c,在module_init時注冊一個文件系統(tǒng)eventpoll_fs_type,對該文件系統(tǒng)提供兩種操作poll和release,所以epoll_create返回的文件句柄可以被poll、select或者被其它epoll epoll_wait。對epoll的操作主要通過三個系統(tǒng)調(diào)用實現(xiàn):
1. sys_epoll_create
2. sys_epoll_ctl
3. sys_epoll_wait
下面結(jié)合源碼講述這三個系統(tǒng)調(diào)用。
1.1 long sys_epoll_create (int size)
該系統(tǒng)調(diào)用主要分配文件句柄、inode以及file結(jié)構(gòu)。在linux-2.4.32內(nèi)核中,使用hash保存所有注冊到該epoll的文件句柄,在該系統(tǒng)調(diào)用中根據(jù)size大小分配hash的大小。具體為不小于size,但小于2*size的2的某次方。最小為2的9次方(512),最大為2的17次方(128 x 1024)。在linux-2.6.10內(nèi)核中,使用紅黑樹保存所有注冊到該epoll的文件句柄,size參數(shù)未使用。
1.2 long sys_epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
1. 注冊句柄 op = EPOLL_CTL_ADD
注冊過程主要包括:
A.將fd插入到hash(或rbtree)中,如果原來已經(jīng)存在返回-EEXIST,
B.給fd注冊一個回調(diào)函數(shù),該函數(shù)會在fd有事件時調(diào)用,在該函數(shù)中將fd加入到epoll的就緒隊列中。
C.檢查fd當(dāng)前是否已經(jīng)有期望的事件產(chǎn)生。如果有,將其加入到epoll的就緒隊列中,喚醒epoll_wait。

2. 修改事件 op = EPOLL_CTL_MOD
修改事件只是將新的事件替換舊的事件,然后檢查fd是否有期望的事件。如果有,將其加入到epoll的就緒隊列中,喚醒epoll_wait。

3. 刪除句柄 op = EPOLL_CTL_DEL
將fd從hash(rbtree)中清除。
1.3 long sys_epoll_wait(int epfd, struct epoll_event *events, int maxevents,int timeout)
如果epoll的就緒隊列為空,并且timeout非0,掛起當(dāng)前進(jìn)程,引起CPU調(diào)度。
如果epoll的就緒隊列不空,遍歷就緒隊列。對隊列中的每一個節(jié)點,獲取該文件已觸發(fā)的事件,判斷其中是否有我們期待的事件,如果有,將其對應(yīng)的epoll_event結(jié)構(gòu)copy到用戶events。

revents = epi->file->f_op->poll(epi->file, NULL);
epi->revents = revents & epi->event.events;
if (epi->revents) {
……
copy_to_user;
……
}
需要注意的是,在LT模式下,把符合條件的事件copy到用戶空間后,還會把對應(yīng)的文件重新掛接到就緒隊列。所以在LT模式下,如果一次epoll_wait某個socket沒有read/write完所有數(shù)據(jù),下次epoll_wait還會返回該socket句柄。
四、 使用epoll的注意事項
1. ET模式比LT模式高效,但比較難控制。
2. 如果某個句柄期待的事件不變,不需要EPOLL_CTL_MOD,但每次讀寫后將該句柄modify一次有助于提高穩(wěn)定性,特別在ET模式。
3. socket關(guān)閉后最好將該句柄從epoll中delete(EPOLL_CTL_DEL),雖然epoll自身有處理,但會使epoll的hash的節(jié)點數(shù)增多,影響搜索hash的速度。
Q:網(wǎng)絡(luò)服務(wù)器的瓶頸在哪?
A:IO效率。

在大家苦苦的為在線人數(shù)的增長而導(dǎo)致的系統(tǒng)資源吃緊上的問題正在發(fā)愁的時候,Linux 2.6內(nèi)核中提供的System Epoll為我們提供了一套完美的解決方案。傳統(tǒng)的select以及poll的效率會因為在線人數(shù)的線形遞增而導(dǎo)致呈二次乃至三次方的下降,這些直接導(dǎo)致了網(wǎng)絡(luò)服務(wù)器可以支持的人數(shù)有了個比較明顯的限制。

自從Linux提供了/dev/epoll的設(shè)備以及后來2.6內(nèi)核中對/dev/epoll設(shè)備的訪問的封裝(System Epoll)之后,這種現(xiàn)象得到了大大的緩解,如果說幾個月前,大家還對epoll不熟悉,那么現(xiàn)在來說的話,epoll的應(yīng)用已經(jīng)得到了大范圍的普及。

那么究竟如何來使用epoll呢?其實非常簡單。
通過在包含一個頭文件#include 以及幾個簡單的API將可以大大的提高你的網(wǎng)絡(luò)服務(wù)器的支持人數(shù)。

首先通過create_epoll(int maxfds)來創(chuàng)建一個epoll的句柄,其中maxfds為你epoll所支持的最大句柄數(shù)。這個函數(shù)會返回一個新的epoll句柄,之后的所有操作將通過這個句柄來進(jìn)行操作。在用完之后,記得用close()來關(guān)閉這個創(chuàng)建出來的epoll句柄。

之后在你的網(wǎng)絡(luò)主循環(huán)里面,每一幀的調(diào)用epoll_wait(int epfd, epoll_event events, int max events, int timeout)來查詢所有的網(wǎng)絡(luò)接口,看哪一個可以讀,哪一個可以寫了。基本的語法為:
nfds = epoll_wait(kdpfd, events, maxevents, -1);
其中kdpfd為用epoll_create創(chuàng)建之后的句柄,events是一個epoll_event*的指針,當(dāng)epoll_wait這個函數(shù)操作成功之后,epoll_events里面將儲存所有的讀寫事件。max_events是當(dāng)前需要監(jiān)聽的所有socket句柄數(shù)。最后一個timeout是 epoll_wait的超時,為0的時候表示馬上返回,為-1的時候表示一直等下去,直到有事件范圍,為任意正整數(shù)的時候表示等這么長的時間,如果一直沒有事件,則范圍。一般如果網(wǎng)絡(luò)主循環(huán)是單獨的線程的話,可以用-1來等,這樣可以保證一些效率,如果是和主邏輯在同一個線程的話,則可以用0來保證主循環(huán)的效率。

epoll_wait范圍之后應(yīng)該是一個循環(huán),遍利所有的事件:

C/C++ code
for(n = 0; n < nfds; ++n) { if(events[n].data.fd == listener) { //如果是主socket的事件的話,則表示有新連接進(jìn)入了,進(jìn)行新連接的處理。 client = accept(listener, (struct sockaddr *) &local, &addrlen); if(client < 0){ perror("accept"); continue; } setnonblocking(client); // 將新連接置于非阻塞模式 ev.events = EPOLLIN | EPOLLET; // 并且將新連接也加入EPOLL的監(jiān)聽隊列。 注意,這里的參數(shù)EPOLLIN | EPOLLET并沒有設(shè)置對寫socket的監(jiān)聽,如果有寫操作的話,這個時候epoll是不會返回事件的,如果要對寫操作也監(jiān)聽的話,應(yīng)該是EPOLLIN | EPOLLOUT | EPOLLET ev.data.fd = client; if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, client, &ev) < 0) { // 設(shè)置好event之后,將這個新的event通過epoll_ctl加入到epoll的監(jiān)聽隊列里面,這里用EPOLL_CTL_ADD來加一個新的 epoll事件,通過EPOLL_CTL_DEL來減少一個epoll事件,通過EPOLL_CTL_MOD來改變一個事件的監(jiān)聽方式。 fprintf(stderr, "epoll set insertion error: fd=%d0, client); return -1; } } else // 如果不是主socket的事件的話,則代表是一個用戶socket的事件,則來處理這個用戶socket的事情,比如說read(fd,xxx)之類的,或者一些其他的處理。 do_use_fd(events[n].data.fd); }

對,epoll的操作就這么簡單,總共不過4個API:epoll_create, epoll_ctl, epoll_wait和close。
如果您對epoll的效率還不太了解,請參考我之前關(guān)于網(wǎng)絡(luò)游戲的網(wǎng)絡(luò)編程等相關(guān)的文章。

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多