epoll哪些触发模式_5.epoll的水平触发和边缘触发
本篇是多路復用的第五篇,主要來講解epoll的水平觸發(fā)和邊緣觸發(fā)是怎么回事。
一、概念介紹
EPOLL事件有兩種模型,水平出發(fā)和邊緣觸發(fā),如下所示:
1. Level Triggered (LT) 水平觸發(fā)
1. socket接收緩沖區(qū)不為空 有數(shù)據(jù)可讀 讀事件一直觸發(fā)2. socket發(fā)送緩沖區(qū)不滿 可以繼續(xù)寫入數(shù)據(jù) 寫事件一直觸發(fā)備注:符合思維習慣,epoll_wait返回的事件就是socket的狀態(tài)例子介紹:
1.?accept一個連接,添加到epoll中監(jiān)聽EPOLLIN事件2.?當EPOLLIN事件到達時,read?fd中的數(shù)據(jù)并處理3.?當需要寫出數(shù)據(jù)時,把數(shù)據(jù)write到fd中;如果數(shù)據(jù)較大,無法一次性寫出,那么在epoll中監(jiān)聽EPOLLOUT事件4.?當EPOLLOUT事件到達時,繼續(xù)把數(shù)據(jù)write到fd中;如果數(shù)據(jù)寫出完畢,那么在epoll中關閉EPOLLOUT事件2. Edge Triggered (ET) 邊沿觸發(fā)
1. socket的接收緩沖區(qū)狀態(tài)變化時觸發(fā)讀事件,即空的接收緩沖區(qū)剛接收到數(shù)據(jù)時觸發(fā)讀事件2. socket的發(fā)送緩沖區(qū)狀態(tài)變化時觸發(fā)寫事件,即滿的緩沖區(qū)剛空出空間時觸發(fā)讀事件備注:僅在狀態(tài)變化時觸發(fā)事件例子介紹:
1.?accept一個一個連接,添加到epoll中監(jiān)聽EPOLLIN|EPOLLOUT事件2.?當EPOLLIN事件到達時,read?fd中的數(shù)據(jù)并處理,read需要一直讀,直到返回EAGAIN為止3.?當需要寫出數(shù)據(jù)時,把數(shù)據(jù)write到fd中,直到數(shù)據(jù)全部寫完,或者write返回EAGAIN4.?當EPOLLOUT事件到達時,繼續(xù)把數(shù)據(jù)write到fd中,直到數(shù)據(jù)全部寫完,或者write返回EAGAIN3.LT和ET兩者比較:
1. 從ET的處理過程中可以看到,ET的要求是需要一直讀寫,直到返回EAGAIN,否則就會遺漏事件。ET的編程可以做到更加簡潔,某些場景下更加高效,但另一方面容易遺漏事件,容易產(chǎn)生bug。2. LT的處理過程中,直到返回EAGAIN不是硬性要求,但通常的處理過程都會讀寫直到返回EAGAIN,但LT比ET多了一個開關EPOLLOUT事件的步驟。LT的編程與poll/select接近,符合一直以來的習慣,不易出錯。二?、內(nèi)核調(diào)度實現(xiàn)方式
在epoll_wait的時候,阻塞等待事件發(fā)生, 事件發(fā)生時通過回調(diào)掛到ready list鏈表中
epoll_wait返回, 處理ready list, 返回事件給調(diào)用者
此時ET模式已經(jīng)將事件從ready list中刪除,LT模式中還存在
此時假設應用程序處理完了事件, 再次epoll_wait. ?ET模式繼續(xù)阻塞
LT模式由于ready list中依然存在事件則不會阻塞, 對這些socket調(diào)用poll方法獲取最新的事件信息,如果確認沒事件了才會刪除。
三、?水平觸發(fā)和邊緣觸發(fā)的常見問題
1. 水平觸發(fā)的問題:不必要的喚醒
內(nèi)核:收到一個新建連接的請求
內(nèi)核:由于 “驚群效應” ,喚醒兩個正在 epoll_wait() 的線程 A 和線程 B
線程A:epoll_wait() 返回
線程B:epoll_wait() 返回
線程A:執(zhí)行 accept() 并且成功
線程B:執(zhí)行 accept() 失敗,accept() 返回 EAGAIN
2. 邊緣觸發(fā)的問題:不必要的喚醒以及饑餓
1)不必要的喚醒:
1.內(nèi)核:收到第一個連接請求。線程 A 和 線程 B 兩個線程都在 epoll_wait() 上等待。由于采用邊緣觸發(fā)模式,所以只有一個線程會收到通知。這里假定線程 A 收到通知2.線程A:epoll_wait() 返回3.線程A:調(diào)用 accpet() 并且成功4.內(nèi)核:此時 accept queue 為空,所以將邊緣觸發(fā)的 socket 的狀態(tài)從可讀置成不可讀5.內(nèi)核:收到第二個建連請求6.內(nèi)核:此時,由于線程 A 還在執(zhí)行 accept() 處理,只剩下線程 B 在等待 epoll_wait(),于是喚醒線程 B。7.線程A:繼續(xù)執(zhí)行 accept() 直到返回 EAGAIN8.線程B:執(zhí)行 accept(),并返回 EAGAIN,此時線程 B 可能有點困惑(“明明通知我有事件,結果卻返回 EAGAIN”)9.線程A:再次執(zhí)行 accept(),這次終于返回 EAGAIN2)饑餓:
1.內(nèi)核:接收到兩個建連請求。線程 A 和 線程 B 兩個線程都在等在 epoll_wait()。由于采用邊緣觸發(fā)模式,只有一個線程會被喚醒,我們這里假定線程 A 先被喚醒2.線程A:epoll_wait() 返回3.線程A:調(diào)用 accpet() 并且成功4.內(nèi)核:收到第三個建連請求。由于線程?A?還沒有處理完(沒有返回?EAGAIN),當前?socket?還處于可讀的狀態(tài),由于是邊緣觸發(fā)模式,所有不會產(chǎn)生新的事件5.線程A:繼續(xù)執(zhí)行 accept() 希望返回 EAGAIN 再進入 epoll_wait() 等待,然而它又 accept() 成功并處理了一個新連接6.內(nèi)核:又收到了第四個建連請求7.線程A:又繼續(xù)執(zhí)行 accept(),結果又返回成功參考文檔:
https://blog.csdn.net/dongfuye/article/details/50880251
https://www.zhihu.com/question/20502870
https://blog.lucode.net/linux/epoll-tutorial.html
https://plantegg.github.io/2019/12/09/epoll%E7%9A%84LT%E5%92%8CET/
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅持創(chuàng)作打卡瓜分現(xiàn)金大獎總結
以上是生活随笔為你收集整理的epoll哪些触发模式_5.epoll的水平触发和边缘触发的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 信息安全管理与评估_计算机工程学院教师参
- 下一篇: 资料分析——基础知识