select,poll,epoll區別總結數組
select,poll,epoll都是I/O多路複用。I/O多路複用就是經過一種機制,能夠監測多個描述符,一旦某個描述就緒(通常是讀或者寫),可以通知程序進行相應的讀寫操做。但select、poll、epoll本質上都是同步I/O,由於他們都須要在讀寫事件就緒後本身負責進行讀寫,也就是說這個讀寫過程是阻塞的,而異步I/O則無需本身負責進行讀寫服務器
,異步I/O的實現會負責把數據從內核拷貝到用戶空間。網絡
I/O多路複用之select總結數據結構
I/O多路複用是指內核一旦發現進程指定的一個或者多個I/O條件準備讀取,它就通知進程。適用於如下場合:多線程
一、當客戶處理多個描述字時(通常是交互式輸入和網絡套接口),必須使用I/O複用。併發
二、當一個客戶同時處理多個套接口時,而這種狀況是可能的,但不多出現。異步
三、若是一個TCP服務器既要處理監聽套接口,又要處理已鏈接套接口,通常也要用到I/O複用。socket
四、若是一個服務器即要處理TCP,又要處理UDP,通常要使用I/O複用。tcp
五、若是一個服務器要處理多個服務或多個協議,通常要使用I/O複用。函數
六、與多進程和多線程技術相比,I/O多路複用技術的最大優點是系統開銷小,系統沒必要建立進程/線程維護這些進程/線程,從而大大減小了系統的開銷。
select的調用過程
一、使用copy_from_user從用戶空間拷貝fd_set到內核空間
二、註冊回調函數__pollwait
三、遍歷全部fd,調用其對應的poll方法(對於socket,這個poll方法是sock_poll,sock_poll根據狀況會調用到tcp_poll,udp_poll或者datagram_poll)
四、以tcp_poll爲例,其核心實現就是__pollwait,也就是上面註冊的回調函數。
五、__pollwait的主要工做就是把current(當前進程)掛到設備的等待隊列中,不一樣的設備有不一樣的等待隊列,對於tcp_poll來講,其等待隊列是sk->sk_sleep(注意把進程掛到等待隊列中並不表明進程已經睡眠了)。在設備收到一條消息(網絡設備)或填寫完文件數據(磁盤設備)後,會喚醒設備等待隊列上睡眠的進程,這時current便被喚醒了。
六、poll方法返回時會返回一個描述讀寫操做是否就緒的mask掩碼,根據這個mask掩碼給fd_set賦值。
七、若是遍歷完全部的fd,尚未返回一個可讀寫的mask掩碼,則會調用schedule_timeout是調用select的進程(也就是current)進入睡眠。當設備驅動發生自身資源可讀寫後,會喚醒其等待隊列上睡眠的進程。若是超過必定的超時時間(schedule_timeout指定),仍是沒人喚醒,則調用select的進程會從新被喚醒得到CPU,進而從新遍歷fd,判斷有沒有就緒的fd。
八、把fd_set從內核空間拷貝到用戶空間。
select的幾大缺點:
一、每次調用select,都須要把fd集合從用戶態拷貝到內核態,這個開銷在fd不少時會很大。
二、同時每次調用select都須要在內核遍歷傳遞進來的fd,這個開銷在fd不少時也很大
三、select支持的文件描述符數量過小了,默認是1024
I/O多路複用之poll總結
poll的實現和select很是類似,只是描述fd集合的方式不一樣,poll使用的是pollfd結構而不是select的fd_set結構,其餘的都差很少,管理多個描述符也是進行輪詢,根據描述符的狀態進行處理,但poll沒有最大文件描述符數量的限制。poll和select一樣存在一個缺點就是,包含大量文件描述符的數組被總體複製於用戶態和內核態的地址空間,而不論這些文件描述符是否就緒,它的開銷隨着文件描述符數量的增長而線性增大。
I/O多路複用之epoll總結
一、mmap
mmap系統調用並非爲了共享內存而設計的,它提供了不一樣於通常對普通文件的訪問方式,進程能夠像都內存同樣對普通文件操做。
mmap系統調用使得進程之間經過映射同一個普通的文件實現共享。普通文件被映射到進程地址空間後,進程能夠像訪問普通內存同樣對文件進行訪問,沒必要再調用read()/write()等操做
mmap並不分配空間,只是將文件映射到嗲用進程的地址空間(佔用虛擬地址空間),而後就可使用memcpy()等操做,內存中文件並不當即更新到文件中,而是由一段時間的延遲,可使用msync()顯式的同步。
優勢:
1.一、提高效率
通常寫文件須要用open 、read、write,須要將磁盤文件讀取到內核的cache緩衝區,而後再拷貝到用戶內空間內存,涉及兩次的讀寫。
mmap經過將磁盤文件映射到用戶空間,當進程讀文件時,發生缺頁中斷,給虛擬內存分配物理內存,在經過磁盤調頁操做將磁盤數據讀到物理內存上面,實現了用戶空間數據的讀取,整個過程只有一次內存的拷貝。
1.2、用於進程間大量的數據通訊
兩個進程映射同一個文件,在兩個進程中,同一個文件去映射的虛擬地址空間不一樣。一個進程操做文件時,先經過缺頁獲取物理地址。進而經過磁盤文件調頁操做將文件讀入到內存。
另外一個進程訪問文件的時候,發現沒有物理頁面映射到虛擬內存,經過FS的缺頁處理查找cache區是否有讀入磁盤文件,有的話創建映射關係,這兩個進程經過共享內存就能夠進行通信了。
mmap把文件映射到內存空間中,簡單的說mmap就是把一個文件的內容作一個映射。映成功後,用戶對這段內存區域的修改就能夠直接反應到內核空間,一樣內核空間對這段區域的修改也能夠直接反應到用戶的空間。那麼對與內核空間------用戶空間二者之間須要大量數據傳輸等操做的話效率是很是高的。
mmap並不分配內存空間,只是將文件映射到調用進程的地址空間裏(可是會佔用你的virutal memory),而後你就能夠用memery等操做寫文件,而不用write()了。寫完後,內存中的文件不會當即更新到文件中,而是有一段時間的延遲,可使用msync()來顯式同步一下,這樣你所寫的內容就能立馬保存到文件裏面了。不過經過mmap來寫文件不能增長文件的長度,應爲映射的長度在調用mmap()的時候就決定了。
原理
首先,映射,就是創建一種對應的關係,主要是在硬盤上文件的位置與進程地址空間中一塊大小相同的區域之間--對應。
在內存映射的過程當中,並無實際數據的拷貝,文件並沒與被載入到內存中,只是邏輯上被放入到了內存,具體到代碼,就是你創建並初始化了相關的數據結構。
實際上,進程之間在共享內存時,並不老是讀寫少許數據後就解除映射,有新進程通信,在從新創建共享內存區域。而是保持共享區域,知道通訊完畢爲止,這樣,數據內容一致保存在共享內存中,並無寫回文件。共享內存中的內容每每是解除映射的時候才寫回文件。所以,採用共享內存的通信方式效率是很高的。
二、紅黑樹
三、鏈表
。。。
epoll既然是對select和poll的改進,就應該能避免上述的三個缺點。那epoll都是怎麼解決的呢?在此以前,咱們先看一下epoll和select和poll的調用接口上的不一樣,select和poll都只提供了一個函數——select或者poll函數。而epoll提供了三個函數,epoll_create,epoll_ctl和epoll_wait,epoll_create是建立一個epoll句柄;epoll_ctl是註冊要監聽的事件類型;epoll_wait則是等待事件的產生。
對於第一個缺點,epoll的解決方案在epoll_ctl函數中。每次註冊新的事件到epoll句柄中時(在epoll_ctl中指定EPOLL_CTL_ADD),會把全部的fd拷貝進內核,而不是在epoll_wait的時候重複拷貝。epoll保證了每一個fd在整個過程當中只會拷貝一次。
對於第二個缺點,epoll的解決方案不像select或poll同樣每次都把current輪流加入fd對應的設備等待隊列中,而只在epoll_ctl時把current掛一遍(這一遍必不可少)併爲每一個fd指定一個回調函數,當設備就緒,喚醒等待隊列上的等待者時,就會調用這個回調函數,而這個回調函數會把就緒的fd加入一個就緒鏈表)。epoll_wait的工做實際上就是在這個就緒鏈表中查看有沒有就緒的fd(利用schedule_timeout()實現睡一會,判斷一會的效果,和select實現中的第7步是相似的)。
對於第三個缺點,epoll沒有這個限制,它所支持的FD上限是最大能夠打開文件的數目,這個數字通常遠大於2048,舉個例子,在1GB內存的機器上大約是10萬左右,具體數目能夠cat /proc/sys/fs/file-max察看,通常來講這個數目和系統內存關係很大。
select、poll、epoll之間的區別總結
select、poll、epoll都是IO多路複用的機制。IO多路複用就是經過一種機制,能夠監測多個描述符,一旦某個描述符就緒(通常是讀就緒或者寫就緒),可以通知程序進行相應的讀寫。select、poll、epoll本職上都是同步I/O。由於他們都須要在讀事件就緒後本身負責讀寫,也就是說這幾個讀寫過程是阻塞的,而異步I/O則無需本身負責讀寫,異步I/O的實現會負責把數據從內核拷貝到用戶空間。
總結
(1)select,poll實現須要本身不斷輪詢全部fd集合,直到設備就緒,期間可能要睡眠和喚醒屢次交替。而epoll其實也須要調用epoll_wait不斷輪詢就緒鏈表,期間也可能屢次睡眠和喚醒交替,可是它是設備就緒時,調用回調函數,把就緒fd放入就緒鏈表中,並喚醒在epoll_wait中進入睡眠的進程。雖然都要睡眠和交替,可是select和poll在「醒着」的時候要遍歷整個fd集合,而epoll在「醒着」的時候只要判斷一下就緒鏈表是否爲空就好了,這節省了大量的CPU時間。這就是回調機制帶來的性能提高。
(2)select,poll每次調用都要把fd集合從用戶態往內核態拷貝一次,而且要把current往設備等待隊列中掛一次,而epoll只要一次拷貝,並且把current往等待隊列上掛也只掛一次(在epoll_wait的開始,注意這裏的等待隊列並非設備等待隊列,只是一個epoll內部定義的等待隊列)。這也能節省很多的開銷。
一、select==>時間複雜度O(n)
它僅僅知道了,有I/O事件發生了,卻並不知道是哪那幾個流(可能有一個,多個,甚至所有),咱們只能無差異輪詢全部流,找出能讀出數據,或者寫入數據的流,對他們進行操做。因此select具備O(n)的無差異輪詢複雜度,同時處理的流越多,無差異輪詢時間就越長。
二、poll==>時間複雜度O(n)
poll本質上和select沒有區別,它將用戶傳入的數組拷貝到內核空間,而後查詢每一個fd對應的設備狀態, 可是它沒有最大鏈接數的限制,緣由是它是基於鏈表來存儲的.
(3)epoll==>時間複雜度O(1)
epoll能夠理解爲event poll,不一樣於忙輪詢和無差異輪詢,epoll會把哪一個流發生了怎樣的I/O事件通知咱們。因此咱們說epoll其實是事件驅動(每一個事件關聯上fd)的,此時咱們對這些流的操做都是有意義的。(複雜度下降到了O(1))
select,poll,epoll都是IO多路複用的機制。I/O多路複用就經過一種機制,能夠監視多個描述符,一旦某個描述符就緒(通常是讀就緒或者寫就緒),可以通知程序進行相應的讀寫操做。但select,poll,epoll本質上都是同步I/O,由於他們都須要在讀寫事件就緒後本身負責進行讀寫,也就是說這個讀寫過程是阻塞的,而異步I/O則無需本身負責進行讀寫,異步I/O的實現會負責把數據從內核拷貝到用戶空間。
epoll跟select都能提供多路I/O複用的解決方案。在如今的Linux內核裏有都可以支持,其中epoll是Linux所特有,而select則應該是POSIX所規定,通常操做系統均有實現
select
select的本質是經過設置或者檢查存放fd標誌位的數據結構來進行下一步處理。這樣帶來的缺點:
一、單個進程可監視的fd數量有限,即能監聽端口號的大小有限。
通常來講這個數目和系統內存關係很大,具體數目能夠cat /proc/sys/fs/file-max察看。32位機默認是1024個。64位機默認是2048.
二、對socket進行掃描是線性掃描,即採用輪訓的方法,效率低:
當套接字比較多的時候,每次select()都要經過遍歷FD_SETSIZE個Socket來完成調度,無論哪一個Socket是活躍的,都遍歷一遍。這會浪費不少CPU時間。若是能給套接字註冊某個回調函數,當他們活躍時,自動完成相關操做,那就避免了輪詢,這正是epoll與kqueue作的。
三、須要維護一個存放大量fd的數據結構,這樣會使得用戶空間和內核空間在傳遞該結構複製開銷大。
poll
poll本質上和select沒有區別,它將用戶傳入的數組拷貝到內核空間,而後查詢每一個fd對應的設備狀態,若是設備就緒則在設備等待隊列中加入一項並繼續遍歷,若是遍歷完全部fd後沒有發現就緒設備,則掛起當前進程,直到設備就緒或者主動超時,被喚醒後它又要再次遍歷fd。這個過程經歷了屢次無謂的遍歷。
它沒有最大鏈接數的限制,緣由是它是基於鏈表來存儲的,可是一樣有一個缺點:
一、大量的fd數組被總體複製於用戶狀態和內核狀態之間,而無論這樣的複製是否是有意義。
二、poll還有一個特色是「水平觸發」,若是報告了fd後,沒有被處理,那麼下次poll時還會再次報告該fd。
epoll
epoll有EPOLLLT和EPOLLET兩種觸發模式,LT是默認的模式,ET是「高速」模式。LT模式下,只要這個fd還有數據可讀,每次 epoll_wait都會返回它的事件,提醒用戶程序去操做,而在ET(邊緣觸發)模式中,它只會提示一次,直到下次再有數據流入以前都不會再提示了,無 論fd中是否還有數據可讀。因此在ET模式下,read一個fd的時候必定要把它的buffer讀光,也就是說一直讀到read的返回值小於請求值,或者 遇到EAGAIN錯誤。還有一個特色是,epoll使用「事件」的就緒通知方式,經過epoll_ctl註冊fd,一旦該fd就緒,內核就會採用相似callback的回調機制來激活該fd,epoll_wait即可以收到通知。
epoll爲何要有EPOLLET模式
若是採用EPOLLLT模式的話,系統中一旦有大量你不須要讀寫的就緒文件描述符,它們每次調用epoll_wait都會返回,這樣會大大下降處理程序檢索本身關心的就緒文件描述符的效率.。而採用EPOLLET這種邊沿觸發模式的話,當被監控的文件描述符上有可讀寫事件發生時,epoll_wait()會通知處理程序去讀寫。若是此次沒有把數據所有讀寫完(如讀寫緩衝區過小),那麼下次調用epoll_wait()時,它不會通知你,也就是它只會通知你一次,直到該文件描述符上出現第二次可讀寫事件纔會通知你!!!這種模式比水平觸發效率高,系統不會充斥大量你不關心的就緒文件描述符。
epoll的優勢
一、沒有最大併發鏈接數的限制,能打開的FD的上限遠大於1024(1G的內存上能監聽約10萬個端口)
二、效率提高,不是輪詢的方式,不會隨着FD數目的增長效率降低。只有活躍可用的FD纔會調用callback函數;
即epoll最大的優勢在於它只管你「活躍的連接」,而跟鏈接總數無關,所以在實際的網絡環境中,Epoll的效率會遠高於select和poll。
三、內存拷貝,利用mmap()文件映射內存加速與內核空間的消息傳遞;即內核epoll使用mmap減小複製的開銷。
select、poll、epoll區別總結:
一、支持一個進程所能打開的最大鏈接數
一、select
單個進程所能打開的最大鏈接數有FD_SETSIZE宏定義,其大小是32個整數的大小(在32位的機器上,大小就是3232,同理64位機器上FD_SETSIZE爲3264),固然咱們能夠對進行修改,而後從新編譯內核,可是性能可能會受到影響,這須要進一步的測試。
2、poll
poll本質上和select沒有區別,可是它沒有最大鏈接數的限制,緣由是它用鏈表來存儲。
三、epoll
雖然鏈接數有上限,1G內存的機器上能夠鏈接10萬左右的鏈接,2G內存的機器能夠打開20萬左右的鏈接
2、fd劇增後帶來的IO效率問題
1、select
由於每次調用都會對鏈接進行線性的遍歷,因此FD的增長會形成遍歷速度的「線性降低性能問題」
二、poll
同上
三、epoll
由於epoll內核中實現是根據每一個fd的callback函數實現的,只有活躍的socket纔會主動調用callback,因此在活躍socket較少的狀況下,使用epoll沒有前面二者的線性降低的性能問題,可是全部socket都很活躍的狀況下,可能會有性能問題。
3、消息傳遞
1、select
內核須要將消息傳遞到用戶空間,都須要內核的拷貝動做
2、poll
同上
三、epoll
經過內核和用戶空間共享一塊內存來實現
總結
一、表面行看epoll的想能最好,可是在鏈接數少而且都十分活躍的狀況下,select和poll的想能可能更好,epoll的通信機制須要不少的函數回調。
二、select低效由於每次它都須要輪訓,可是低效也是相對的,視狀況決定。