1. 程式人生 > >Apache select與Nginx epoll模型區別

Apache select與Nginx epoll模型區別

Linux服務

1.select 和epoll模型區別
1.1.網絡IO模型概述
通常來說,網絡IO可以抽象成用戶態和內核態之間的數據交換。
一次網絡數據讀取操作(read),可以拆分成兩個步驟:
1)網卡驅動等待數據準備好(內核態)
2)將數據從內核空間拷貝到進程空間(用戶態)。根據這兩個步驟處理方式不一樣,我們通常把網絡IO劃分成阻塞IO和非阻塞IO。
·阻塞IO。用戶調用網絡IO相關的系統調用時(例如read),如果此時內核網卡還沒有讀取到網絡數據,那麽本次系統調用將會一直阻塞,直到對端系統發送的數據到達為止。如果對端一直沒有發送數據,則本次調用將永遠不會返回。
· 非阻塞IO。當用戶調用網絡IO相關的系統調用時(例如read),如果此時內核網絡還沒有收到網絡數據,那麽本次系統調用將會立即返回,並返回一個EAGAIN的錯誤碼。
在沒有IO多路復用技術之前,由於沒有一種好的方式來探測網絡IO是否可讀可寫。因此,為了增加系統的並發連接量,一般是借助多線程或多進程的方式來增加系統的並發連接數。但是這種方式有個問題就是系統的並發連接數受限於操作系統的最大線程或進程數,並且隨著操作系統的線程或進程數增加,將會引發大量的上下文切換,導致系統的性能急劇下降。為了解決這個問題,操作系統引入了IO多路轉接技術(IO multiplexing)。

1.2. IO多路轉接技術
IO多路轉接技術其實就是使用select、epoll等操作系統提供的系統調用來檢測IO事件的各種機制。通過select、epoll等機制,我們可以很輕松的同時管理大量的網絡IO連接,並且獲取到處於活躍狀態的連接。當其中一個或多個發生網絡IO事件時,select、epoll等系統調用就會返回相應的連接,我們就可以對這些連接進行讀取或寫入操作,從而完成網絡數據交互。

1.3.select 工作原理
select函數原型:
int select(int nfds, fd_set readfds, fd_set writefds,fd_set exceptfds, struct timeval timeout);
select各個參數說明:
· nfds
這個參數的值一般設置為讀集合(readfds)、寫集合(writefds)以及exceptfds(異常集合)中最大的描述符(fd)+1,當然也可以設置為FD_SETSIZE。FD_SETSIZE是操作系統定義的一個宏,一般是1024。也就是說讀寫以及異常集合大小的最大值是1024,所以使用select最多只能管理1024個連接。如果大於1024個連接,select將會產生不確定行為。

· readfds
指向可讀描述符集的指針,如果我們關心連接的可讀事件,需要把連接的描述符設置到讀集合中。
·writefds
指向可寫描述符集的指針,如果我們關心連接的可寫事件,需要把連接的描述符設置到可寫集合中。
· exceptfds
指向異常描述符集的指針,如果我們關心連接的是否發生異常,需要把連接的描述符設置到異常描述符集合中。
·timeout
指select願意等待的時間。
struct timeval {
longtv_sec; //秒數
longtv_usec; //微秒數
}
一般來說,分為三種情況:
·timeout為空,select將會永遠等待。直到有連接可讀、可寫或者被信號中斷時返回。
·timeout->tv_sec = 0 且 timeout->tv_usec = 0,完全不等待。檢測所有指定的描述符後立即返回。這是得到多個描述符的狀態而不阻塞select函數的輪詢方法。
·timeout->tv_sec != 且 timeout->tv_usec != 0,等待指定的秒數和微秒數。當指定的描述符之一已經準備好,或者超過了指定的時間值,則立即返回。如果超時了,還沒有一個描述符準備好,則返回0。
select的工作原理,select通過輪詢來檢測各個集合中的描述符(fd)的狀態,如果描述符的狀態發生改變,則會在該集合中設置相應的標記位;如果指定描述符的狀態沒有發生改變,則將該描述符從對應集合中移除。因此,select的調用復雜度是線性的,即O(n)。舉個例子,一個保姆照看一群孩子,如果把孩子是否需要尿尿比作網絡IO事件,select的作用就好比這個保姆挨個詢問每個孩子:你要尿尿嗎?如果孩子回答是,保姆則把孩子拎出來放到另外一個地方。當所有孩子詢問完之後,保姆領著這些要尿尿的孩子去上廁所(處理網絡IO事件)。
select的限制,前面提到FD_SETSIZE宏,這個宏是操作系統定義的。在linux下面通常是1024,也就是說select最多只能管理1024個描述符。如果大於1024的個描述,select將會產生不可預知的行為。那在沒有poll或epoll的情況下,怎樣使用select來處理連接數大於1024的情況呢?答案是使用多線程技術,每個線程單獨使用一個select進行檢測。這樣的話,你的系統能夠處理的並發連接數等於線程數1024。早期的apache就是這種技術來支撐海量連接的。
1.4.epoll工作原理
epoll函數原型:
int epoll_create(int size);
intepoll_ctl(int epfd, int op, int fd, struct epoll_event
event);
int epoll_wait(intepfd, struct epoll_event *events, intmaxevents, int timeout);
epoll依賴上述三個函數,既可以完成成千上萬的並發連接管理。epoll使用方式,1)通過epoll_create建立epoll句柄。2)將描述符所感興趣的事件通過epoll_ctl添加到epoll句柄中。3)調用epoll_wait返回所有可讀寫的描述符。
epoll是Linux內核為處理大批量文件描述符而作了改進的epoll,是Linux下多路復用IO接口select/poll的增強版本,它能顯著提高程序在大量並發連接中只有少量活躍的情況下的系統CPU利用率。另一點原因就是獲取事件的時候,它無須遍歷整個被偵聽的描述符集,只要遍歷那些被內核IO事件異步喚醒而加入Ready隊列的描述符集合就行了。epoll除了提供select/poll那種IO事件的水平觸發(Level Triggered)外,還提供了邊緣觸發(Edge Triggered),這就使得用戶空間程序有可能緩存IO狀態,減少epoll_wait/epoll_pwait的調用,提高應用程序效率。
還是以保姆照看一群孩子為例,在epoll機制下,保姆不再需要挨個的詢問每個孩子是否需要尿尿。取而代之的是,每個孩子如果自己需要尿尿的時候,自己主動的站到事先約定好的地方,而保姆的職責就是查看事先約定好的地方是否有孩子。如果有小孩,則領著孩子去上廁所(網絡事件處理)。因此,epoll的這種機制,能夠高效的處理成千上萬的並發連接,而且性能不會隨著連接數增加而下降。

Apache select與Nginx epoll模型區別