前言:關(guān)于并發(fā)服務(wù)器中的I/O復(fù)用實(shí)現(xiàn)方式,前面我們講過(guò)select的方式,但select的性能比較低,其實(shí)不合適以Web服務(wù)器端開發(fā)為主流的現(xiàn)代開發(fā)環(huán)境。因此就有了Linux下的epoll,BSD的kqueue,Solaris的/dev/poll和Windows的IOCP等復(fù)用技術(shù)。本章就來(lái)說(shuō)講Linux下的epoll技術(shù)。
基于select的I/O復(fù)用技術(shù)速度慢的緣由:
1,調(diào)用select函數(shù)后常見的針對(duì)所有文件描寫符的循環(huán)語(yǔ)句。它每次事件產(chǎn)生需要遍歷所有文件描寫符,找動(dòng)身生變化的文件描寫符。(之前寫的示例沒(méi)加循環(huán))
2,每次調(diào)用select函數(shù)時(shí)都需要向該函數(shù)傳遞監(jiān)視對(duì)象信息。即每次調(diào)用select函數(shù)時(shí)向操作系統(tǒng)傳遞監(jiān)視對(duì)象信息,至于為何要傳?是由于我們監(jiān)視的套接字變化的函數(shù),而套接字是操作系統(tǒng)管理的。(這個(gè)才是最耗效力的)
注釋:基于這樣的緣由其實(shí)不是說(shuō)select就沒(méi)用了,在這樣的情況下就合適選用select:1,服務(wù)端接入者少 2,程序應(yīng)具有兼容性。
epoll是怎樣優(yōu)化select問(wèn)題的:
1,每次產(chǎn)生事件它不需要循環(huán)遍歷所有文件描寫符,它把產(chǎn)生變化的文件描寫符單獨(dú)集中到了1起。
2,僅向操作系統(tǒng)傳遞1次監(jiān)視對(duì)象信息,監(jiān)視范圍或內(nèi)容產(chǎn)生變化時(shí)只通知產(chǎn)生變化的事項(xiàng)。
實(shí)現(xiàn)epoll時(shí)必要的函數(shù)和結(jié)構(gòu)體
函數(shù):
epoll_create:創(chuàng)建保存epoll文件描寫符的空間,該函數(shù)也會(huì)返回文件描寫符,所以終止時(shí),也要調(diào)用close函數(shù)。(創(chuàng)建內(nèi)存空間)epoll_ctl:向空間注冊(cè),添加或修改文件描寫符。(注冊(cè)監(jiān)聽事件)
epoll_wait:與select函數(shù)類似,等待文件描寫符產(chǎn)生變化。(監(jiān)聽事件回調(diào))
結(jié)構(gòu)體:
struct epoll_event
{
__uint32_t events;
epoll_data_t data;
}typedef union epoll_data
{
void *ptr;
int fd;
__uinit32_t u32;
__uint64_t u64;
} epoll_data_t;
甚么是條件觸發(fā)和邊沿觸發(fā)?它們是指事件響應(yīng)的方式,epoll默許是條件觸發(fā)的方式。條件觸發(fā)是指:只要輸入緩沖中有數(shù)據(jù)就會(huì)1直通知該事件,循環(huán)響應(yīng)epoll_wait。而邊沿觸發(fā)是指:輸入緩沖收到數(shù)據(jù)時(shí)僅注冊(cè)1次該事件,即便輸入緩沖中還留有數(shù)據(jù),也不會(huì)再進(jìn)行注冊(cè),只響應(yīng)1次。
邊沿觸發(fā)相對(duì)條件觸發(fā)的優(yōu)點(diǎn):可以分離接收數(shù)據(jù)和處理數(shù)據(jù)的時(shí)間點(diǎn),從實(shí)現(xiàn)模型的角度看,邊沿觸發(fā)更有可能帶來(lái)高性能。
將上面epoll實(shí)例改成邊沿觸發(fā):
1,首先改寫 event.events = EPOLLIN | EPOLLET; (EPOLLIN:讀取數(shù)據(jù)事件 EPOLLET:邊沿觸發(fā)方式)
2,邊沿觸發(fā)只響應(yīng)1次接收數(shù)據(jù)事件,所以要1次性全部讀取輸入緩沖中的數(shù)據(jù),那末就需要判斷甚么時(shí)候數(shù)據(jù)讀取完了?Linux聲明了1個(gè)全局的變量:int errno; (error.h中),它能記錄產(chǎn)生毛病時(shí)提供額外的信息。這里就能夠用它來(lái)判斷是不是讀取完數(shù)據(jù):
3,邊沿觸發(fā)方式下,以阻塞方式工作的read&write有可能會(huì)引發(fā)服務(wù)真?zhèn)€長(zhǎng)時(shí)間停頓。所以邊沿觸發(fā)1定要采取非阻塞的套接字?jǐn)?shù)據(jù)傳輸情勢(shì)。那末怎樣將套接字的read,write數(shù)據(jù)傳輸情勢(shì)修改成非阻塞模式呢?