提到epoll,就不得不提到select,poll了,因為epoll解決的問題,正是select,poll遇到的問題。
Linux提供了select、poll、epoll接口來實現(xiàn)IO復(fù)用,三者的原型如下所示,本文從參數(shù)、實現(xiàn)、性能等方面對三者進行對比
intselect(intnfds,fd_set*readfds,fd_set*writefds,fd_set*exceptfds,structtimeval*timeout);intpoll(structpollfd*fds,nfds_tnfds,inttimeout);intepoll_wait(intepfd,structepoll_event*events,intmaxevents,inttimeout);
select、poll、epoll_wait參數(shù)及實現(xiàn)對比
1.select的第一個參數(shù)nfds為fdset集合中最大描述符值加1,fdset是一個位數(shù)組,其大小限制為__FD_SETSIZE(1024),位數(shù)組的每一位代表其對應(yīng)的描述符是否需要被檢查。
select的第二三四個參數(shù)表示需要關(guān)注讀、寫、錯誤事件的文件描述符位數(shù)組,這些參數(shù)既是輸入?yún)?shù)也是輸出參數(shù),可能會被內(nèi)核修改用于標(biāo)示哪些描述符上發(fā)生了關(guān)注的事件。所以每次調(diào)用select前都需要重新初始化fdset。
timeout參數(shù)為超時時間,該結(jié)構(gòu)會被內(nèi)核修改,其值為超時剩余的時間。select對應(yīng)于內(nèi)核中的sys_select調(diào)用,sys_select首先將第二三四個參數(shù)指向的fd_set拷貝到內(nèi)核,然后對每個被SET的描述符調(diào)用進行poll,并記錄在臨時結(jié)果中(fdset),如果有事件發(fā)生,select會將臨時結(jié)果寫到用戶空間并返回;當(dāng)輪詢一遍后沒有任何事件發(fā)生時,如果指定了超時時間,則select會睡眠到超時,睡眠結(jié)束后再進行一次輪詢,并將臨時結(jié)果寫到用戶空間,然后返回。select返回后,需要逐一檢查關(guān)注的描述符是否被SET(事件是否發(fā)生)。
2.poll與select不同,通過一個pollfd數(shù)組向內(nèi)核傳遞需要關(guān)注的事件,故沒有描述符個數(shù)的限制,pollfd中的events字段和revents分別用于標(biāo)示關(guān)注的事件和發(fā)生的事件,故pollfd數(shù)組只需要被初始化一次。
poll的實現(xiàn)機制與select類似,其對應(yīng)內(nèi)核中的sys_poll,只不過poll向內(nèi)核傳遞pollfd數(shù)組,然后對pollfd中的每個描述符進行poll,相比處理fdset來說,poll效率更高。
poll返回后,需要對pollfd中的每個元素檢查其revents值,來得指事件是否發(fā)生。
3.epoll通過epoll_create創(chuàng)建一個用于epoll輪詢的描述符,通過epoll_ctl添加/修改/刪除事件,通過epoll_wait檢查事件,epoll_wait的第二個參數(shù)用于存放結(jié)果。
epoll返回后,該參數(shù)指向的緩沖區(qū)中即為發(fā)生的事件,對緩沖區(qū)中每個元素進行處理即可,而不需要像poll、select那樣進行輪詢檢查。