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

Apache select和Nginx epoll模型區別

部分內容摘自跟老男孩學Linux運維:Web叢集實戰(運維人員必備書籍)

  /2561410/1752270

1.select epoll模型

.IO模型概述

     通常來,網IO可以抽象成使用者態和核心態之間的資料交換。一次網路資料讀取操作(read),可以拆分成兩個步驟:1)網絡卡驅動等待資料準備好(核心態)2)將資料從核心空間拷貝到程序空間(使用者態)。根據這兩個步驟處理方式不一樣,我們通常把網路IO劃分成阻塞IO和非阻塞IO。

      ·阻塞IO。用戶調用網IO相關的系統調(例如read),如果此核心網絡卡沒有取到網資料,那麼本次系統呼叫將會一直阻塞,直到對端系統傳送的資料到達為止。如果對端一直沒有傳送資料,則本次呼叫將永遠不會返回。

      ·非阻塞IO。當用戶調用網IO相關的系統調(例如read),如果此核心網絡還沒有收到網資料,那麼本次系統調用將會立即返回,並返回一個EAGAIN錯誤碼

在沒有IO多路複用技之前,由於沒有一種好的方式來探IO是否可可寫。因此,了增加系的並發連線量,一般是藉助多程或多程的方式來增加系的並發連線數。但是種方式有個問題就是系的並發連接數受限於操作系的最大程或程數,並且隨著操作系程或程數增加,將會引大量的上下文切致系的效能急下降。瞭解決問題,操作系引入了IO多路接技IO multiplexing)。

.IO多路
技術

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

工作原理

       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就是這種技術來支撐海量連線的。

工作原理

  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返回所有可寫的描述符。

  epollLinux核心為處理大批量檔案描述符而作了改epoll,是Linux下多路複用IO介面select/poll的增版本,它能著提高程式在大量發連中只有少量活的情況下的系CPU利用率。另一點原因就是取事件的候,它無整個被聽的描述符集,只要遍那些被核心IO事件非同步醒而加入Ready列的描述符集合就行了。epoll除了提供select/poll那種IO事件的水平觸Level Triggered)外,提供了邊緣Edge Triggered),就使得用程式有可能IO,減少epoll_wait/epoll_pwait調用,提高用程式效率。

還是以保姆照看一群孩子為例,在epoll機制下,保姆不再需要挨個的詢問每個孩子是否需要尿尿。取而代之的是,每個孩子如果自己需要尿尿的時候,自己主動的站到事先約定好的地方,而保姆的職責就是檢視事先約定好的地方是否有孩子。如果有小孩,則領著孩子去上廁所(網路事件處理)。因此,epoll的這種機制,能夠高效的處理成千上萬的併發連線,而且效能不會隨著連線數增加而下降。

epoll

上所述,selectepoll比如下表所示

select

epoll

效能

隨著接數增加,急下降。理成千上萬並發連接數,效能很差。

隨著接數增加,效能基本上沒有下降。理成千上萬並發連,效能很好。

接數

連線數有限制,處理的最大連線數不超過1024。如果要處理超過1024個連線數,則需要修改FD_SETSIZE巨集,並重新編譯 。

接數無限制。

內在處理機制

線性輪詢

回撥callback

開發複雜性

老男孩教育最新課程selectepoll簡單區別比喻

select的呼叫複雜度是線性的,即O(n)。舉個例子,一個保姆照看一群孩子,如果把孩子是否需要尿尿比作網路IO事件,select的作用就好比這個保姆挨個詢問每個孩子:你要尿尿嗎?如果孩子回答是,保姆則把孩子拎出來放到另外一個地方。當所有孩子詢問完之後,保姆領著這些要尿尿的孩子去上廁所(處理網路IO事件)。

還是以保姆照看一群孩子為例,在epoll機制下,保姆不再需要挨個的詢問每個孩子是否需要尿尿。取而代之的是,每個孩子如果自己需要尿尿的時候,自己主動的站到事先約定好的地方,而保姆的職責就是檢視事先約定好的地方是否有孩子。如果有小孩,則領著孩子去上廁所(網路事件處理)。因此,epoll的這種機制,能夠高效的處理成千上萬的併發連線,而且效能不會隨著連線數增加而下降。