導航:首頁 > 編程語言 > javaepoll模型

javaepoll模型

發布時間:2023-03-31 15:40:42

1. Handler 中的 epoll

linux 中,epoll 機制是一個重要的機制。在 Android 中的 Handler,簡單的利用了 epoll 機制,做到了消息隊列的阻塞和喚醒。

epoll 機制相關的函數有

因為對於Handler 對爛帶於 epoll 沒有過於深入的使用,只是利用了 epoll 進行了阻塞和喚醒,還是比較好理解的。

於是,便利用 epoll 機制在mEpollFd上添加(EPOLL_CTL_ADD)了監聽的 fd(mWakeEventFd);

java 層,next( )@Message 會阻塞到nativePollOnce(long ptr, int timeoutMillis),特別的是,當沒有消息時,timeoutMillis = -1表示一直阻塞。如果有 delay 的消息,則 timeoutMillis 表示 delay的時間。

此時利用epoll 機制在 epoll_wait()上設置超時時間。當 timeoutMillis = -1時會一直等待知道有新消息來。否則當超時時間到達時信歷賀,會返回到 next()@Message就可以處理那條 delay 的消息了。

當有新消息來臨時並且是立刻執行的,enqueueMessage()@Message 會調用nativeWake(),否則會根據新來的消息的 delay 時滑派間和隊列中的 delay 時間進行對比,消息隊列是按照msg 的到達時間和 delay 時間進行排序,如果新來的消息在前並且需要 delay 也會進行 wake()

當往 mWakeEventFd 寫入一個 1,便會從 epoll_wait() 馬上返回。進行新一輪的消息處理。

另外,native 層的 Looper 的 epoll 機制沒有這么簡單,只是在 Handler 中只是簡單地使用了。

Linux中的epoll

2. epoll 丟連接或丟包問題

問: 服務端用epoll 模型 , 客戶端連接服務端發送大量udp 數據包, 經過一段時間後, 再次發送udp 數據包時,服務端收不到包,(或者說大量tcp連接服務端,服務端會丟失連接),為何?

答:1. 在服務端,用 ss -lu 查看 對應進程的recv-Q 是否占滿

當 Recv-Q 可以理解為 對應進程socket 的接收緩存隊列,系統維護,如果占滿,系統會丟棄數據包。 可dmesg 查看相應系統日誌。

epoll 模型有兩種觸發方式:基慶拆
一種叫好比拉尿,拉尿的時候都是一次拉完,只要膀胱中還有尿,就一直拉。這就是epoll 模型中的EPOLLLT (水平觸發)模式,只要緩沖區里有數據,就一直觸發差漏,(應用)趕快處理。
一種叫拉屎, 你完全可以拉到一半,然後提褲子走人,去接個電話。過一會兒,可能又有了拉屎的欲搏棗望, 再去拉屎。這就是epoll 模型中的EPOLLET(邊沿觸發)模式,當兩個網路包同時到達時,只觸發一次。

3. java Netty NIO 如何突破 65536 個埠的限制如何做到10萬~50萬的長連接

通常情況下是不可以突破的,埠有限制.單獨對外提供請求的服務不用考慮埠數量問題,監聽某一個埠即可.但是向提供代理伺服器,就不得不考慮埠數量受限問題了.當前的1M並發連接測試,也需要在客戶端突破6萬可用埠的限制.埠為16進制,那麼2的16次方值為65536,在linux系統裡面,1024以下埠都是超級管理員用戶(如root)才可以使用,普通用戶只能使用大於1024的埠值.

伺服器是只監聽一個埠,所有的客戶端連接,都是連接到伺服器的同一個埠上的。也就是說伺服器只是用了一個埠。就比如Http伺服器。默認只用了80埠。

nio 在linux上使用的是epoll ,epoll支持在一個進程中打開的FD是操作系統最大文件句柄數,而不是你所說的16位short表示的文件句柄。 而 select模型 單進程打開的FD是受限的 select模型默認FD是1024 。操作系統最大文件句柄數跟內存有關,1GB內存的機器上,大概是10萬個句柄左右。

4. 什麼是 IO 模型

伺服器端編程經常需要構造高性能的IO模型,常見的IO模型有四種:
(1)同步阻塞IO(Blocking IO):即傳統的IO模型。

(2)同步非阻塞IO(Non-blocking IO):默認創建的socket都是阻塞的,非阻塞IO要求socket被設置為NONBLOCK。注意這里所說的NIO並非Java的NIO(New IO)庫。

(3)IO多路復用(IO Multiplexing):即經典的Reactor設計模式,有時也稱為非同步阻塞IO,Java中的Selector和Linux中的epoll都是這種模型。

(4)非同步IO(Asynchronous IO):即經典的Proactor設計模式,也稱為非同步非阻塞IO。

5. java網路io模型有幾種

#BIO---Blocking IO
- 每個socket一個線程,讀寫時線程處於阻塞狀態。
優點:實現簡單
缺點:無法滿足高並發,高接入的需求

- 不使用線程池的BIO模型,除了無法滿足高並發需求外,由於需要為每個請求創建一個線程,還可能因為接入大量不活躍連接而耗盡伺服器資源。

- 使用線程池的BIO模型,雖然控制了線程數量,但由於其本質上讀寫仍是阻塞的,仍無法滿足高並發需求。

#NIO---Non-Blocking IO(非阻塞IO)
##非阻塞IO和多路復用
非阻塞IO和多路復用實際上是兩個不用的概念,由於兩者通常結合在一起使用,因此兩者往往被混為一談。下面我將試著分清這兩個概念:
###非阻塞IO
與BIO相對應,非阻塞IO的讀寫方法無論是否有數據都立即返回,因此可以通過輪詢方式來實現,但輪詢方式的效率並不比BIO有顯著提高,因為每個連接仍然需要佔用一個線程。下面是輪詢方式實現的IO模式圖:

###多路復用
- 多路復用結合非阻塞IO能夠明顯提高IO的效率,這也是Java1.4把非阻塞IO和多路復用同時發布的原因。
- 多路復用的核心是多路復用器(Selector),它是需要操作系統底層支持的,簡單的說,就是進程把多個socket和它們關心的事件(比如連接請求或數據已准備好)都注冊在多路復用器上,操作系統會在事件發生時通知多路復用器,這樣進程就可以通過多路復用器知道在那個socket上發生了什麼時間,從而進行對應的處理。
- 多路復用的優點在於只需要一個線程監測(阻塞或輪詢方式均可)多路選擇器的狀態,只有在有事件需要發生時才會真正的創建線程進行處理,因此更適合高並發多接入的應用環境。

- 在Linux系統下,多路復用的底層實現是epoll方法,與select/poll的順序掃描不同,epoll採用效率更高的事件驅動方式,而且epoll方式並沒有socket個數限制。
##BIO和NIO的比較
- BIO適用於連接長期保持的應用,比如一個復雜系統中模塊之間通過長連接來進行通信。
- NIO加多路復用的模式更適合短連接、高並發、多接入的情形,比如網路伺服器。
##NIO網路編程的常用介面
##Reactor模式
Reactor模式用於解決事件分發處理的問題,Handler把自己的channel和關注的事件注冊到Selector中,當對應的事件發生在自己的channel上時,對應的handler就會得到通知並進行處理。
- 單線程的Reactor
消息的分發、讀寫、處理都在一個線程中處理,是Reactor最簡單的實現方式,如果消息的處理需要較長時間,會影響效率。

```java

//Reactor類,負責分發事件並調用對應的handler
class Reactor implements Runnable {

final Selector selector;

final ServerSocketChannel serverSocket;

//Reactor初始化

Reactor(int port) throws IOException {

selector = Selector.open();

serverSocket = ServerSocketChannel.open();

serverSocket.socket().bind(new InetSocketAddress(port));

serverSocket.configureBlocking(false); //必須配置為非阻塞

//Acceptor會在Reactor初始化時就注冊到Selector中,用於接受connect請求
SelectionKey sk = serverSocket.register(selector, SelectionKey.OP_ACCEPT);

sk.attach(new Acceptor()); //attach callback object, Acceptor

}

//分發消息並調用對應的handler
public void run() {
try {

while (!Thread.interrupted()) {

selector.select();

Set selected = selector.selectedKeys();

Iterator it = selected.iterator();

while (it.hasNext())

dispatch((SelectionKey)(it.next()); //Reactor負責dispatch收到的事件

selected.clear();

}

} catch (IOException ex) { /* ... */ }

}

void dispatch(SelectionKey k) {

Runnable r = (Runnable)(k.attachment()); //調用之前注冊的callback對象

if (r != null)

r.run();

}

//Acceptor也是一個handler,負責創建socket並把新建的socket也注冊到selector中

class Acceptor implements Runnable { // inner

public void run() {

try {

SocketChannel c = serverSocket.accept();

if (c != null)

new Handler(selector, c);

}

catch(IOException ex) { /* ... */ }

}

}

}

//Concrete Handler:用於收發和處理消息。
//在當前的實現中,使用Runnable介面作為每個具體Handler的統一介面
//如果在處理時需要參數和返回值,也可以為Handler另外聲明一個統一介面來代替Runnable介面
final class Handler implements Runnable {

final SocketChannel socket;

final SelectionKey sk;

ByteBuffer input = ByteBuffer.allocate(MAXIN);

ByteBuffer output = ByteBuffer.allocate(MAXOUT);

static final int READING = 0, SENDING = 1;

int state = READING;

Handler(Selector sel, SocketChannel c) throws IOException {

socket = c; c.configureBlocking(false);

// Optionally try first read now

sk = socket.register(sel, 0);

sk.attach(this); //將Handler作為callback對象

sk.interestOps(SelectionKey.OP_READ); //第二步,接收Read事件

sel.wakeup();

}

boolean inputIsComplete() { /* ... */ }

boolean outputIsComplete() { /* ... */ }

void process() { /* ... */ }

public void run() {

try {

if (state == READING) read();

else if (state == SENDING) send();

} catch (IOException ex) { /* ... */ }

}

void read() throws IOException {

socket.read(input);

if (inputIsComplete()) {

process();

state = SENDING;

// Normally also do first write now

sk.interestOps(SelectionKey.OP_WRITE); //第三步,接收write事件

}

}

void send() throws IOException {

socket.write(output);

if (outputIsComplete()) sk.cancel(); //write完就結束了, 關閉select key

}

}

//上面 的實現用Handler來同時處理Read和Write事件, 所以裡面出現狀態判斷

//我們可以用State-Object pattern來更優雅的實現

class Handler { // ...

public void run() { // initial state is reader

socket.read(input);

if (inputIsComplete()) {

process();

sk.attach(new Sender()); //狀態遷移, Read後變成write, 用Sender作為新的callback對象

sk.interest(SelectionKey.OP_WRITE);

sk.selector().wakeup();

}

}

class Sender implements Runnable {

public void run(){ // ...

socket.write(output);

if (outputIsComplete()) sk.cancel();

}

}

}

```
- 多線程Reacotr
處理消息過程放在其他線程中執行

```java
class Handler implements Runnable {

// uses util.concurrent thread pool

static PooledExecutor pool = new PooledExecutor(...);

static final int PROCESSING = 3;

// ...

synchronized void read() { // ...

socket.read(input);

if (inputIsComplete()) {

state = PROCESSING;

pool.execute(new Processer()); //使用線程pool非同步執行

}

}

synchronized void processAndHandOff() {

process();

state = SENDING; // or rebind attachment

sk.interest(SelectionKey.OP_WRITE); //process完,開始等待write事件

}

class Processer implements Runnable {

public void run() { processAndHandOff(); }

}

}

```
- 使用多個selector
mainReactor只負責處理accept並創建socket,多個subReactor負責處理讀寫請求

```java
Selector[] selectors; //subReactors集合, 一個selector代表一個subReactor

int next = 0;

class Acceptor { // ...

public synchronized void run() { ...

Socket connection = serverSocket.accept(); //主selector負責accept

if (connection != null)

new Handler(selectors[next], connection); //選個subReactor去負責接收到的connection

if (++next == selectors.length) next = 0;

}

}

```
#AIO
AIO是真正的非同步IO,它於JDK1.7時引入,它和NIO的區別在於:
- NIO仍然需要一個線程阻塞在select方法上,AIO則不需要
- NIO得到數據准備好的消息以後,仍然需要自己把消息復制到用戶空間,AIO則是通過操作系統的支持把數據非同步復制到用戶空間以後再給應用進程發出信號。

6. java目前流行的並發庫有哪些

1、本地仔悶常用庫java.util.concurrent
2、並發的宏戚睜關鍵點:線程、鎖、模型
3、並發常用模型 actor、master-worker、epoll等,均可用本地庫實現
4、第三方依賴庫:akka、蔽歲netty、GCD等

7. IO模型及select,poll,epoll和kqueue的區別

(一)首先,介紹幾種常見的I/O模型及其區別,如下:
blocking I/O
nonblocking I/O
I/O multiplexing (select and poll)
signal driven I/O (SIGIO)
asynchronous I/O (the POSIX aio_functions)—————非同步IO模型最大的特點是 完成後發回通知。
阻塞與否,取決於實現IO交換的方式。
非同步阻塞是基於select,select函數本身的實現方式是阻塞的,而採用select函數有個好處就是它可以同時監聽多個文件句柄.
非同步非阻塞直接在完成後通知,用戶進程只需要發起一個IO操作然後立即返回,等IO操作真正的完成以後,應用程序會得到IO操作完成的通知,此時用戶進程只需要對數據進行處理就好了,不需要進行實際的IO讀寫操作,因為真正的IO讀取或者寫入操作已經由內核完成了。

1 blocking I/O
這個不用多解釋吧,阻塞套接字。下圖是它調用過程的圖示:

重點解釋下上圖,下面例子都會講到。首先application調用 recvfrom()轉入kernel,注意kernel有2個過程,wait for data和 data from kernel to user。直到最後 complete後,recvfrom()才返回。此過程一直是阻塞的。

2 nonblocking I/O:
與blocking I/O對立的,非阻塞套接字,調用過程圖如下:

可以看見,如果直接操作它,那就是個輪詢。。直到內核緩沖區有數據。

3 I/O multiplexing (select and poll)
最常見的I/O復用模型,select。

select先阻塞,有活動套接字才返回。與blocking I/O相比,select會有兩次系統調用,但是select能處理多個套接字。

4 signal driven I/O (SIGIO)
只有UNIX系統支持,感興趣的課查閱相關資料

與I/O multiplexing (select and poll)相比,它的優勢是,免去了select的阻塞與輪詢,當有活躍套接字時,由注冊的handler處理。

5 asynchronous I/O (the POSIX aio_functions)
很少有*nix系統支持,windows的IOCP則是此模型

完全非同步的I/O復用機制,因為縱觀上面其它四種模型,至少都會在由kernel data to appliction時阻塞。而該模型是當完成後才通知application,可見是純非同步的。好像只有windows的完成埠是這個模型,效率也很出色。
6 下面是以上五種模型的比較

可以看出,越往後,阻塞越少,理論上效率也是最優。
=====================分割線==================================
5種模型的比較比較清晰了,剩下的就是把select,epoll,iocp,kqueue按號入座那就OK了。
select和iocp分別對應第3種與第5種模型,那麼epoll與kqueue呢?其實也於select屬於同一種模型,只是更高級一些,可以看作有了第4種模型的某些特性,如callback機制。
為什麼epoll,kqueue比select高級?
答案是,他們無輪詢。因為他們用callback取代了。想想看,當套接字比較多的時候,每次select()都要通過遍歷FD_SETSIZE個Socket來完成調度,不管哪個Socket是活躍的,都遍歷一遍。這會浪費很多CPU時間。如果能給套接字注冊某個回調函數,當他們活躍時,自動完成相關操作,那就避免了輪詢,這正是epoll與kqueue做的。
windows or *nix (IOCP or kqueue/epoll)?

誠然,Windows的IOCP非常出色,目前很少有支持asynchronous I/O的系統,但是由於其系統本身的局限性,大型伺服器還是在UNIX下。而且正如上面所述,kqueue/epoll 與 IOCP相比,就是多了一層從內核數據到應用層的阻塞,從而不能算作asynchronous I/O類。但是,這層小小的阻塞無足輕重,kqueue與epoll已經做得很優秀了。
提供一致的介面,IO Design Patterns
實際上,不管是哪種模型,都可以抽象一層出來,提供一致的介面,廣為人知的有ACE,Libevent(基於reactor模式)這些,他們都是跨平台的,而且他們自動選擇最優的I/O復用機制,用戶只需調用介面即可。說到這里又得說說2個設計模式,Reactor and Proactor。見:Reactor模式--VS--Proactor模式。Libevent是Reactor模型,ACE提供Proactor模型。實際都是對各種I/O復用機制的封裝。
Java nio包是什麼I/O機制?
現在可以確定,目前的java本質是select()模型,可以檢查/jre/bin/nio.dll得知。至於java伺服器為什麼效率還不錯。。我也不得而知,可能是設計得比較好吧。。-_-。
=====================分割線==================================
總結一些重點:
只有IOCP是asynchronous I/O,其他機制或多或少都會有一點阻塞。
select低效是因為每次它都需要輪詢。但低效也是相對的,視情況而定,也可通過良好的設計改善
epoll, kqueue、select是Reacor模式,IOCP是Proactor模式。
java nio包是select模型。。
(二)epoll 與select的區別

1. 使用多進程或者多線程,但是這種方法會造成程序的復雜,而且對與進程與線程的創建維護也需要很多的開銷。(Apache伺服器是用的子進程的方式,優點可以隔離用戶) (同步阻塞IO)

2.一種較好的方式為I/O多路轉接(I/O multiplexing)(貌似也翻譯多路復用),先構造一張有關描述符的列表(epoll中為隊列),然後調用一個函數,直到這些描述符中的一個准備好時才返回,返回時告訴進程哪些I/O就緒。select和epoll這兩個機制都是多路I/O機制的解決方案,select為POSIX標准中的,而epoll為Linux所特有的。

區別(epoll相對select優點)主要有三:
1.select的句柄數目受限,在linux/posix_types.h頭文件有這樣的聲明:#define __FD_SETSIZE 1024 表示select最多同時監聽1024個fd。而epoll沒有,它的限制是最大的打開文件句柄數目。

2.epoll的最大好處是不會隨著FD的數目增長而降低效率,在selec中採用輪詢處理,其中的數據結構類似一個數組的數據結構,而epoll是維護一個隊列,直接看隊列是不是空就可以了。epoll只會對"活躍"的socket進行操作---這是因為在內核實現中epoll是根據每個fd上面的callback函數實現的。那麼,只有"活躍"的socket才會主動的去調用 callback函數(把這個句柄加入隊列),其他idle狀態句柄則不會,在這點上,epoll實現了一個"偽"AIO。但是如果絕大部分的I/O都是「活躍的」,每個I/O埠使用率很高的話,epoll效率不一定比select高(可能是要維護隊列復雜)。

3.使用mmap加速內核與用戶空間的消息傳遞。無論是select,poll還是epoll都需要內核把FD消息通知給用戶空間,如何避免不必要的內存拷貝就很重要,在這點上,epoll是通過內核於用戶空間mmap同一塊內存實現的。

關於epoll工作模式ET,LT

epoll有兩種工作方式
ET:Edge Triggered,邊緣觸發。僅當狀態發生變化時才會通知,epoll_wait返回。換句話,就是對於一個事件,只通知一次。且只支持非阻塞的socket。
LT:Level Triggered,電平觸發(默認工作方式)。類似select/poll,只要還有沒有處理的事件就會一直通知,以LT方式調用epoll介面的時候,它就相當於一個速度比較快的poll.支持阻塞和不阻塞的socket。

三 Linux並發網路編程模型

1 Apache 模型,簡稱 PPC ( Process Per Connection ,):為每個連接分配一個進程。主機分配給每個連接的時間和空間上代價較大,並且隨著連接的增多,大量進程間切換開銷也增長了。很難應對大量的客戶並發連接。
2 TPC 模型( Thread Per Connection ):每個連接一個線程。和PCC類似。
3 select 模型:I/O多路復用技術。
.1 每個連接對應一個描述。select模型受限於 FD_SETSIZE即進程最大打開的描述符數linux2.6.35為1024,實際上linux每個進程所能打開描數字的個數僅受限於內存大小,然而在設計select的系統調用時,卻是參考FD_SETSIZE的值。可通過重新編譯內核更改此值,但不能根治此問題,對於百萬級的用戶連接請求 即便增加相應 進程數, 仍顯得杯水車薪呀。
.2select每次都會掃描一個文件描述符的集合,這個集合的大小是作為select第一個參數傳入的值。但是每個進程所能打開文件描述符若是增加了 ,掃描的效率也將減小。
.3內核到用戶空間,採用內存復制傳遞文件描述上發生的信息。
4 poll 模型:I/O多路復用技術。poll模型將不會受限於FD_SETSIZE,因為內核所掃描的文件 描述符集合的大小是由用戶指定的,即poll的第二個參數。但仍有掃描效率和內存拷貝問題。
5 pselect模型:I/O多路復用技術。同select。
6 epoll模型:
.1)無文件描述字大小限制僅與內存大小相關
.2)epoll返回時已經明確的知道哪個socket fd發生了什麼事件,不用像select那樣再一個個比對。
.3)內核到用戶空間採用共享內存方式,傳遞消息。
四 :FAQ
1、單個epoll並不能解決所有問題,特別是你的每個操作都比較費時的時候,因為epoll是串列處理的。 所以你有還是必要建立線程池來發揮更大的效能。
2、如果fd被注冊到兩個epoll中時,如果有時間發生則兩個epoll都會觸發事件。
3、如果注冊到epoll中的fd被關閉,則其會自動被清除出epoll監聽列表。
4、如果多個事件同時觸發epoll,則多個事件會被聯合在一起返回。
5、epoll_wait會一直監聽epollhup事件發生,所以其不需要添加到events中。
6、為了避免大數據量io時,et模式下只處理一個fd,其他fd被餓死的情況發生。linux建議可以在fd聯繫到的結構中增加ready位,然後epoll_wait觸發事件之後僅將其置位為ready模式,然後在下邊輪詢ready fd列表。

8. java nio和socket的select epoll有什麼區別

當一個節點和多個節點建立連接時,如何高效的處理多個連接的數據,下面具體分析兩者的區別。

  1. select函數

  2. 函數原型:int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

  3. 參數介紹:(1)nfds -- fdset集合中最大描述符值加1

  4. (2)fdset -- 一個位數組,其大小限制為_FD_SETSIZE(1024)

  5. 位數組的每一位代表的是其對應的描述符是否需要被檢查。

  6. (3)readfds -- 讀事件文件描述符數組

  7. (4 )writefds -- 寫事件文件描述符數組

  8. (5)exceptfds -- 錯誤事件文件描述符數組

  9. (6)timeout -- 超時事件,該結構被內核修改,其值為超時剩餘時間。

  10. 對應內核:select對應於內核中的sys_select調用,sys_select首先將第二三四個參數指向的fd_set拷貝到內核,然後對每個被SET的描 述符調用進行poll,並記錄在臨時結果中(fdset),如果有事件發生,select會將臨時結果寫到用戶空間並返回;當輪詢一遍後沒有任何事件發生時,如果指定了超時時間,則select會睡眠到超時,睡眠結束後再進行一次輪詢,並將臨時結果寫到用戶空間,然後返

  11. 2. select/poll特點

  12. 傳統的select/poll每次調用都會線性掃描全部的集合,導致效率呈現線性下降。

  13. poll的執行分三部分:

  14. (1).將用戶傳入的pollfd數組拷貝到內核空間,因為拷貝操作和數組長度相關,時間上這是一個O(n)操作

  15. (2).查詢每個文件描述符對應設備的狀態,如果該設備尚未就緒,則在該設備的等待隊列中加入一項並繼續查詢下一設備的狀態。 查詢完所有設備後如果沒有一個設備就緒,這時則需要掛起當前進程等待,直到設備就緒或者超時。設備就緒後進程被通知繼續運行,這時再次遍歷所有設備,以查找就緒設備。這一步因為兩次遍歷所有設備,時間復雜度也是O(n),這裡面不包括等待時間

  16. (3). 將獲得的數據傳送到用戶空間並執行釋放內存和剝離等待隊列等善後工作,向用戶空間拷貝數據與剝離等待隊列等操作的的時間復雜度同樣是O(n)。

  17. 3. epoll機制

Linux 2.6內核完全支持epoll。epoll的IO效率不隨FD數目增加而線性下降。

要使用epoll只需要這三個系統調用:epoll_create(2), epoll_ctl(2), epoll_wait(2)

epoll用到的所有函數都是在頭文件sys/epoll.h中聲明的,內核實現中epoll是根據每個fd上面的callback函數實現的。只有"活躍"的socket才會主動的去調用 callback函數,其他idle狀態socket則不會。


如果所有的socket基本上都是活躍的---比如一個高速LAN環境,過多使用epoll,效率相比還有稍微的下降。但是一旦使用idle connections模擬WAN環境,epoll的效率就遠在select/poll之上了。

3.1 所用到的函數:

(1)、int epoll_create(int size)

該函數生成一個epoll專用的文件描述符,其中的參數是指定生成描述符的最大范圍

(2)、int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)

用於控制某個文件描述符上的事件,可以注冊事件,修改事件,刪除事件。

如果調用成功返回0,不成功返回-1

int epoll_ctl{

int epfd,//由 epoll_create 生成的epoll專用的文件描述符

int op, //要進行的操作例如注冊事件,可能的取值EPOLL_CTL_ADD 注冊、

//EPOLL_CTL_MOD 修改、EPOLL_CTL_DEL 刪除

int fd, //關聯的文件描述符

struct epoll_event *event//指向epoll_event的指針

}

(3)、int epoll_wait(int

epfd, struct epoll_event *

events,int maxevents, int

timeout)

用於輪詢I/O事件的發生,返回發生事件數

int epoll_wait{

int epfd,//由epoll_create 生成的epoll專用的文件描述符

struct epoll_event * events,//用於回傳代處理事件的數組

int maxevents,//每次能處理的事件數

int timeout//等待I/O事件發生的超時值

//為0的時候表示馬上返回,為-1的時候表示一直等下去,直到有事件

//為任意正整數的時候表示等這么長的時間,如果一直沒有事件

//一般如果網路主循環是單獨的線程的話,可以用-1來等,這樣可以保證一些效率

//如果是和主邏輯在同一個線程的話,則可以用0來保證主循環的效率

}

epoll是為處理大批量句柄而作了改進的poll。

4. epoll的優點:


<1>支持一個進程打開大數目的socket描述符(FD)

select 最不能忍受的是一個進程所打開的FD是有一定限制的,由FD_SETSIZE設置,默認值是2048。對於那些需要支持的上萬連接數目的IM伺服器來說顯然太少了。這時候可以:

(1) 可以修改這個宏然後重新編譯內核,不過資料也同時指出,這樣也會帶來網路效率的下降

(2) 可以選擇多進程的解決方案,不過雖然linux上創建進程的代價比較下,但是仍舊是不可忽視的,所以也不是很完美的方案

epoll沒有這樣的限制,它所支持的FD上限是最大可以打開文件的數目,這個數字一般遠大於2048,具體數組可以查看cat /proc/sys/fs/file-max查看,這個數目和系統內存關系很大。

<2>IO效率不隨FD數目增加而線性下降

傳統的select/poll另一個致命弱點就是當你擁有一個很大的socket集合,不過由於網路延時,任一時間只有部分的socket是"活躍"的,但是select/poll每次調用都會線性掃描全部的集合,導致效率呈現線性下降。

epoll不存在這個問題,它只會對「活躍」的socket進行操作。

這是因為在內核實現中epoll是根據每個fd上面的callback函數實現的。那麼,只有"活躍"的socket才會主動的去調用 callback函數,其他idle狀態socket則不會,在這點上,epoll實現了一個"偽"AIO,因為這時候推動力在os內核。在一些 benchmark中,如果所有的socket基本上都是活躍的---比如一個高速LAN環境,epoll並不比select/poll有什麼效率,相 反,如果過多使用epoll_ctl,效率相比還有稍微的下降。但是一旦使用idle connections模擬WAN環境,epoll的效率就遠在select/poll之上了。

<3>使用mmap加速內核與用戶空間的消息傳遞這點實際上涉及到epoll的具體實現了。無論是select,poll還是epoll都需要內核把FD消息通知給用戶空間,如何避免不必要的內存拷貝就 很重要,在這點上,epoll是通過內核於用戶空間mmap同一塊內存實現的。而如果你想我一樣從2.5內核就關注epoll的話,一定不會忘記手工 mmap這一步的。

<4>內核微調

這一點其實不算epoll的優點了,而是整個linux的優點。也許你可以懷 疑linux,但是你無法迴避linux賦予你微調內核的能力。比如,內核TCP/IP協議棧使用內存池管理sk_buff結構,那麼可以在運行 時期動態調整這個內存pool(skb_head_pool)的大小--- 通過echo XXXX>/proc/sys/net/core/hot_list_length完成。再比如listen函數的第2個參數(TCP完成3次握手 的數據包隊列長度),也可以根據你內存大小動態調整。更甚至在一個數據包面數目巨大但同時每個數據包本身大小卻很小的特殊系統上嘗試最新的NAPI網 卡驅動架構。

9. epoll為什麼這么快epoll的實現原理是什麼

以一個生活中的例子來解釋.假設你在大學中讀書,要等待一個朋友來訪,而這個朋友只知道你在A號樓,但是不知道你具體住在哪裡,於是你們約好了在A號樓門口見面.如雀派早果你使用的阻塞IO模型來處理這個問題,那麼你就只能一直守候在A號樓門口等待朋友的到來,...

10. I/O--多路復用的三種機制Select,Poll和Epoll對比

select、poll 和 epoll 都是 Linux API 提供的 IO 復用方式。

多進程和多線程技術相比,I/O多路復用技術的最大優勢是系統開銷小,系統不必創建進程/線程,也不必維護這些進程/線程,從而大大減小了系統的開銷。

我們先分析一下select函數

int select(int maxfdp1,fd_set *readset,fd_set *writeset,fd_set *exceptset,const struct timeval *timeout);

【參數說明】

int maxfdp1 指定待測試的文件描述字個數,它的值是待測試的最大描述字加1。

fd_set *readset , fd_set *writeset , fd_set *exceptset

fd_set可以理解為一個集合,這喚鬧個集合中存放的是文件描述符(file descriptor),即文件句柄。中間的三個參數指定我們要讓內核測試讀、寫和異常條件的文件描述符集合。如果對某一個的條件不感興趣,就可以把它設為空指針。

const struct timeval *timeout timeout告知內核等待所指定文件描述符集合中的任何一個就緒可花多少時間。其timeval結構用於指定這段時世首間的秒數和微秒數。

【返回值】

int 若有就緒描述符返回其數目,若超時則為0,若出錯則為-1

select()的機制中提供一種fd_set的數據結構,實際上是一個long類型的數組,每一個數組元素都能與一打開的文件句柄(不管是Socket句柄,還是其他文件或命名管道或設備句柄)建立聯系,建立聯系的工作由程序員完成,當調用select()時,由內核根據IO狀態修改fd_set的內容,由此來通知執行了select()的進程哪一Socket或文件可讀。

從流程上來看,使用select函數進行IO請求和同步阻塞模型沒有太大的區別,甚至還多了添加監視socket,以及調用select函數的額外操作,效率更差。但是,使用select以後最大的優勢是用戶可以在一個線程內同時處理多個socket的IO請求。用戶可以注冊多個socket,然後不斷地調用select讀取被激活的socket,即可達到在同一個線程內同時處理多個IO請求的目的。而在同步阻塞模型中,必須通過多線程的方式才能達到這個目的。

poll的機制與select類似,與select在本質上沒有多大差別,管理多個描述符也是進行輪詢,根據描述符的狀態進行處理,但是poll沒有最大文件描述符數量的限制。也就是說,poll只解決了上面的問題3,並沒有解決問題1,2的性能開銷問題。

下面是pll的函數原型:

poll改變了文件描述符集合的描述方式,使用了pollfd結構而不是select的fd_set結構,使得poll支持的文件描述符集合限制遠大於select的1024

【參數說明】

struct pollfd *fds fds是一個struct pollfd類型的數組,用於存放需要檢測其狀態的socket描述符,並且調用poll函數之後fds數組不會被清空;一個pollfd結構體表示一個被監視的文件描述符,通過傳遞fds指示 poll() 監視多個文件描述符。其中,結構體的events域是監視該文件描述符的事件掩碼,由用戶來設置這個域,結構體的revents域是文件描述符的操作結果事件掩碼,內核在調用返回時設置這個域

nfds_t nfds 記錄數組fds中描述符的總數量

【返回值】

int 函數返回fds集合中就緒的讀、寫,或出錯的描述符數量,和返罩返回0表示超時,返回-1表示出錯;

epoll在Linux2.6內核正式提出,是基於事件驅動的I/O方式,相對於select來說,epoll沒有描述符個數限制,使用一個文件描述符管理多個描述符,將用戶關心的文件描述符的事件存放到內核的一個事件表中,這樣在用戶空間和內核空間的只需一次。

Linux中提供的epoll相關函數如下:

1. epoll_create 函數創建一個epoll句柄,參數size表明內核要監聽的描述符數量。調用成功時返回一個epoll句柄描述符,失敗時返回-1。

2. epoll_ctl 函數注冊要監聽的事件類型。四個參數解釋如下:

epoll_event 結構體定義如下:

3. epoll_wait 函數等待事件的就緒,成功時返回就緒的事件數目,調用失敗時返回 -1,等待超時返回 0。

epoll是Linux內核為處理大批量文件描述符而作了改進的poll,是Linux下多路復用IO介面select/poll的增強版本,它能顯著提高程序在大量並發連接中只有少量活躍的情況下的系統CPU利用率。原因就是獲取事件的時候,它無須遍歷整個被偵聽的描述符集,只要遍歷那些被內核IO事件非同步喚醒而加入Ready隊列的描述符集合就行了。

epoll除了提供select/poll那種IO事件的水平觸發(Level Triggered)外,還提供了邊緣觸發(Edge Triggered),這就使得用戶空間程序有可能緩存IO狀態,減少epoll_wait/epoll_pwait的調用,提高應用程序效率。

LT和ET原本應該是用於脈沖信號的,可能用它來解釋更加形象。Level和Edge指的就是觸發點,Level為只要處於水平,那麼就一直觸發,而Edge則為上升沿和下降沿的時候觸發。比如:0->1 就是Edge,1->1 就是Level。

ET模式很大程度上減少了epoll事件的觸發次數,因此效率比LT模式下高。

一張圖總結一下select,poll,epoll的區別:

epoll是Linux目前大規模網路並發程序開發的首選模型。在絕大多數情況下性能遠超select和poll。目前流行的高性能web伺服器Nginx正式依賴於epoll提供的高效網路套接字輪詢服務。但是,在並發連接不高的情況下,多線程+阻塞I/O方式可能性能更好。

既然select,poll,epoll都是I/O多路復用的具體的實現,之所以現在同時存在,其實他們也是不同 歷史 時期的產物

閱讀全文

與javaepoll模型相關的資料

熱點內容
手機辦公有哪些免費app 瀏覽:533
esj輕小說網站怎麼下載小說 瀏覽:735
電影院和女朋友牽手的圖片 瀏覽:20
無毒看片的網站 瀏覽:86
電影電視劇在線免費網站 瀏覽:897
重裝系統後舊系統文件 瀏覽:413
word中表格如何清除文件格式 瀏覽:686
填空什麼的網路 瀏覽:214
可以看電影院上映的電影的網站 瀏覽:3
香港蘋果手機保修政策 瀏覽:950
文件怎麼去掉水印 瀏覽:13
有個國外大尺度電影孕婦分娩的叫什麼 瀏覽:467
怎麼停止資料庫的服務和進程 瀏覽:463
征途裝備靈魂鎖鏈物防11怎樣升級 瀏覽:56
桌面的文件怎麼排成一排 瀏覽:846
wow鍛造怎麼升級 瀏覽:338
選編程和學ps哪個好 瀏覽:447
他和誰睡了主演 瀏覽:944
彩票過濾軟體用什麼語言編程好 瀏覽:637
wps如何把文件變成excel 瀏覽:577

友情鏈接