select原理
- 將 文件描述符集合 fd_set 從用戶空間拷貝到內(nèi)核空間井联,進入內(nèi)核態(tài)
- 遍歷所有的文件描述符,對每個文件描述符調(diào)用 poll(), 該函數(shù)返回一組標準掩碼娄猫,其中各個位指示相對應的文件描述符的就緒狀態(tài)(全為0表示沒有任何事件就緒),根據(jù)掩碼對 fd_set 進行賦值,如果沒有文件就緒且時間未到則會進入短暫的睡眠衫生,然后再恢復并再次對 fd_set 輪詢調(diào)用 poll(), 如此反復,直到有文件就緒或者時間到達土浸。
- 只要有文件就緒罪针,函數(shù)就返回, 將fd_set 再次從內(nèi)核空間拷貝到用戶空間黄伊,系統(tǒng)返回用戶態(tài)泪酱,用戶就可以對相應的文件進行讀和寫了
epoll原理
epoll 一共有三個函數(shù),分別是 epoll_create() , epoll_ctl(),
epoll_wait()还最。
下面介紹三個函數(shù)的原理
epoll_create()
該函數(shù)建立一個epoll句柄
內(nèi)核在epoll文件系統(tǒng)中建立了一個 file節(jié)點
在內(nèi)核緩沖區(qū) Cache 中建立一棵紅黑樹和一條雙向鏈表墓阀, 紅黑樹用來存儲以后 epoll_ctl() 注冊的文件描述符, 雙向鏈表用來存放就緒的文件描述符憋活。
epoll_ctl()
該函數(shù)是功能是將被監(jiān)聽的文件描述符添加到epoll句柄岂津,或者從epoll句柄中刪除,或者更改文件描述符的監(jiān)聽狀態(tài)
將對應的文件描述符添加到紅黑樹中悦即,或者從紅黑樹中刪除吮成,當將文件描述添加到紅黑樹時,并給內(nèi)核中斷函數(shù)注冊一條回調(diào)函數(shù)辜梳,告訴內(nèi)核粱甫,如果該文件描述符就緒,將其添加到就緒鏈表中
epoll_wait()
該函數(shù)返回就緒的文件描述和就緒數(shù)目的大小
該函數(shù)觀察就緒鏈表中的數(shù)據(jù)作瞄,有數(shù)據(jù)就返回茶宵,沒數(shù)據(jù)就sleep,直到有事件發(fā)生或者事件到達
select 和 epoll 的優(yōu)缺點:
select 缺點
- 最大并發(fā)數(shù)有限制宗挥,默認數(shù)量是1024個
- 每次都會線性地掃描整個 文件描述符集合 fd_set, 集合越大乌庶,速度越慢,效率延展性差
- 每次調(diào)用都需要在用戶空間和內(nèi)核空間來回的拷貝契耿,而且每次調(diào)用select需要重新初始化 fd_set
epoll 效率提升
- 最大并發(fā)數(shù)量沒有限制瞒大,依賴于系統(tǒng)所能打開的最大文件數(shù)目限制
- 只有已經(jīng)就緒的文件才會主動觸發(fā)回調(diào)函數(shù),效率和文件數(shù)目大小無關搪桂,性能延展性好
- 省去了不必要的拷貝
當然以上優(yōu)點是在特定環(huán)境下:
高并發(fā)透敌,且任意時間只有少量的文件是活躍的,如果并發(fā)量低,文件活躍地多酗电,select就不一定比 epoll 慢魄藕。
文件描述符何時就緒
正確使用select和epoll需要理解在什么情況下,文件描述符會表示為就緒態(tài)撵术。
SUSv3中說:如果對I/O函數(shù)的調(diào)用不會被阻塞背率,而不論該函數(shù)是否能夠?qū)嶋H傳輸數(shù)據(jù),此時文件描述符(未指定 O_NONBLOCK 標志)被認為是就緒的荷荤。
select 和 epoll 只會告訴我們 I/O 操作是否會阻塞退渗, 而不是告訴我們到底能否成功傳輸數(shù)據(jù)。
epoll的水平觸發(fā)LT和邊緣觸發(fā)ET
默認情況下蕴纳,epoll提供的是水平觸發(fā)通知,表示epoll會告訴我們何時能在文件描述符上以非阻塞的方式執(zhí)行 I/O 操作个粱。 這和 select所提供的通知類型相同
epoll的邊緣觸發(fā)通知古毛,表示自從上一次調(diào)用epoll_wait() 依賴,文件上是否已經(jīng)有 I/O活動了都许,如果有多個I/O 事件發(fā)生的話稻薇,epoll將它們合并成一次單獨的通知, 通過epoll_wait() 返回胶征。 注意: 這里不同于信號驅(qū)動 I/O, 信號驅(qū)動 I/O中會產(chǎn)生多個信號塞椎。
想象下面一種情景
使用epoll監(jiān)視一個套接字上的輸入 (EPOLLIN), 接下來會發(fā)生如下事件。
- 套接字上有輸入的到來睛低。
- 調(diào)用一次 epoll_wait()案狠。無論采用 LT 還是 ET , 該調(diào)用都會告訴我們套接字已經(jīng)處于就緒狀態(tài)。
- 再次調(diào)用 epoll_wait()
如果是 LT 模式钱雷,函數(shù)仍將直接返回骂铁,告訴我們套接字處于 就緒 狀態(tài)。
如果是 ET 模式罩抗, 函數(shù)將 阻塞拉庵, 因為自從第一次 調(diào)用 epoll_wait() 之后,并沒有新的輸入到來套蒂。
邊緣觸發(fā)通知通常和非阻塞文件描述符結合使用