在IO多路複用技術中,epoll預設的事件觸發模式為Level_triggered(水平觸發)模式,即當被監控的檔案描述符上有可讀/寫事件發生時,epoll_wait()會通知處理程式去讀寫。如果這次沒有把資料一次性全部讀寫完(如讀寫緩衝區太小),那麼下次呼叫 epoll_wait()時,它還會通知你在上沒讀寫完的檔案描述符上繼續讀寫,當然如果你一直不去讀寫,會一直通知!如果系統中有大量你不需要讀寫的就緒檔案描述符,而它們每次都會返回,這樣會大大降低處理程式檢索自己關心的就緒檔案描述符的效率。
Edge_triggered(邊緣觸發):當被監控的檔案描述符上有可讀寫事件發生時,epoll_wait()會通知處理程式去讀寫。如果這次沒有把資料全部讀寫完(如讀寫緩衝區太小),那麼下次呼叫epoll_wait()時,它不會通知你,也就是它只會通知你一次,直到該檔案描述符上出現第二次可讀寫事件才會通知!這種模式比水平觸發效率高,系統不會充斥大量你不關心的就緒檔案描述符!
阻塞IO:當讀一個阻塞的檔案描述符時,如果在該檔案描述符上沒有資料可讀,那麼它會一直阻塞(通俗一點就是一直卡在呼叫函式那裡),直到有資料可讀。當你去寫一個阻塞的檔案描述符時,如果在該檔案描述符上沒有空間(通常是緩衝區)可寫,那麼它會一直阻塞,直到有空間可寫。以上的讀和寫統一指在某個檔案描述符進行的操作,不單單指真正的讀資料,寫資料,還包括接收連線accept(),發起連線connect()等操作。
非阻塞IO:當你去讀寫一個非阻塞的檔案描述符時,不管可不可以讀寫,它都會立即返回,返回成功說明讀寫操作完成了,返回失敗會設定相應errno狀態碼,根據這個errno可以進一步執行其他處理。它不會像阻塞IO那樣,程序阻塞在函式呼叫那裡不動!
注意:epoll在不設定EPOLLET時,預設的事件觸發模式為水平觸發模式;socket在不設定非阻塞(nonblock)引數時,預設是阻塞的。
1 水平觸發(LT)模式結合阻塞IO(block)
可以看出在這種模式下,程式會阻塞在epoll_wait()函式這裡,只有客服端有新的資料傳送到服務端,EPOLLOUT事件(伴隨著EPOLLIN事件)才會觸發:
1)讀緩衝區能夠一次存放客戶端傳送過來的資料時,只觸發一次EPOLLOUT事件。
2)讀緩衝區不能一次存放客戶端傳送過來的資料時,會多次觸發EPOLLOUT事件,直到讀完客服端傳送過來的資料。
2 水平觸發(LT)模式結合非阻塞IO(nonblock)
在水平觸發模式下,採用非阻塞IO情況下,程式不會阻塞在epoll_wait()這裡,而是隻要客服端與服務端建立的連線可用,即使客服端沒有傳送資料,EPOLLOUT事件也會一直觸發(寫緩衝區可寫)。
3 邊緣觸發(ET)模式結合阻塞IO(block)
採用邊緣觸發模式,在阻塞IO的情況下,客服端傳送過來的資料小於讀緩衝區大小時,程式會一直阻塞在recv()函式(具體時間轉入sk_wait_data函式,直到客戶端有新資料傳送過來或者達到超時才會跳出此函式)這裡,導致程式無法進行下一步操作。
4 邊緣觸發(ET)模式結合非阻塞IO(nonblock)
邊緣觸發模式下采用非阻塞IO,具體地說,採用邊緣觸發模式必須使用非阻塞IO,不然讀緩衝區資料沒有讀滿就會一直阻塞在recv()函式這裡;改成了非阻塞IO之後,當讀緩衝區資料沒有讀滿或者讀到的資料為空時,recv()函式就會返回errno程式碼EAGAIN 或者 EWOULDBLOCK,進而做相應的後續處理。
具體程式碼參見:https://gitlab.com/JC_peng/epoll_test