日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > linux >内容正文

linux

Linux下epoll如何实现高效处理百万句柄的

發(fā)布時間:2025/3/21 linux 15 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux下epoll如何实现高效处理百万句柄的 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

開發(fā)高性能網(wǎng)絡(luò)程序時,windows開發(fā)者們言必稱iocp,linux開發(fā)者們則言必稱epoll。大家都明白epoll是一種IO多路復(fù)用技術(shù),可以非常高效的處理數(shù)以百萬計的socket句柄,比起以前的select和poll效率高大發(fā)了。我們用起epoll來都感覺挺爽,確實快,那么,它到底為什么可以高速處理這么多并發(fā)連接呢?

先簡單回顧下如何使用C庫封裝的3個epoll系統(tǒng)調(diào)用吧。

  • int?epoll_create(int?size);??
  • int?epoll_ctl(int?epfd,?int?op,?int?fd,?struct?epoll_event?*event);??
  • int?epoll_wait(int?epfd,?struct?epoll_event?*events,int?maxevents,?int?timeout);??
  • 使用起來很清晰,首先要調(diào)用epoll_create建立一個epoll對象。參數(shù)size是內(nèi)核保證能夠正確處理的最大句柄數(shù),多于這個最大數(shù)時內(nèi)核可不保證效果。

    epoll_ctl可以操作上面建立的epoll,例如,將剛建立的socket加入到epoll中讓其監(jiān)控,或者把 epoll正在監(jiān)控的某個socket句柄移出epoll,不再監(jiān)控它等等。

    epoll_wait在調(diào)用時,在給定的timeout時間內(nèi),www.linuxidc.com?當(dāng)在監(jiān)控的所有句柄中有事件發(fā)生時,就返回用戶態(tài)的進(jìn)程。

    從上面的調(diào)用方式就可以看到epoll比select/poll的優(yōu)越之處:因為后者每次調(diào)用時都要傳遞你所要監(jiān)控的所有socket給select/poll系統(tǒng)調(diào)用,這意味著需要將用戶態(tài)的socket列表copy到內(nèi)核態(tài),如果以萬計的句柄會導(dǎo)致每次都要copy幾十幾百KB的內(nèi)存到內(nèi)核態(tài),非常低效。而我們調(diào)用epoll_wait時就相當(dāng)于以往調(diào)用select/poll,但是這時卻不用傳遞socket句柄給內(nèi)核,因為內(nèi)核已經(jīng)在epoll_ctl中拿到了要監(jiān)控的句柄列表。

    所以,實際上在你調(diào)用epoll_create后,內(nèi)核就已經(jīng)在內(nèi)核態(tài)開始準(zhǔn)備幫你存儲要監(jiān)控的句柄了,每次調(diào)用epoll_ctl只是在往內(nèi)核的數(shù)據(jù)結(jié)構(gòu)里塞入新的socket句柄。

    在內(nèi)核里,一切皆文件。所以,epoll向內(nèi)核注冊了一個文件系統(tǒng),用于存儲上述的被監(jiān)控socket。當(dāng)你調(diào)用epoll_create時,就會在這個虛擬的epoll文件系統(tǒng)里創(chuàng)建一個file結(jié)點。當(dāng)然這個file不是普通文件,它只服務(wù)于epoll。

    epoll在被內(nèi)核初始化時(操作系統(tǒng)啟動),同時會開辟出epoll自己的內(nèi)核高速cache區(qū),用于安置每一個我們想監(jiān)控的socket,這些socket會以紅黑樹的形式保存在內(nèi)核cache里,以支持快速的查找、插入、刪除。這個內(nèi)核高速cache區(qū),就是建立連續(xù)的物理內(nèi)存頁,然后在之上建立slab層,簡單的說,就是物理上分配好你想要的size的內(nèi)存對象,每次使用時都是使用空閑的已分配好的對象。

  • static?int?__init?eventpoll_init(void)??
  • {??
  • ????...?...??
  • ??
  • ????/*?Allocates?slab?cache?used?to?allocate?"struct?epitem"?items?*/??
  • ????epi_cache?=?kmem_cache_create("eventpoll_epi",?sizeof(struct?epitem),??
  • ????????????0,?SLAB_HWCACHE_ALIGN|EPI_SLAB_DEBUG|SLAB_PANIC,??
  • ????????????NULL,?NULL);??
  • ??
  • ????/*?Allocates?slab?cache?used?to?allocate?"struct?eppoll_entry"?*/??
  • ????pwq_cache?=?kmem_cache_create("eventpoll_pwq",??
  • ????????????sizeof(struct?eppoll_entry),?0,??
  • ????????????EPI_SLAB_DEBUG|SLAB_PANIC,?NULL,?NULL);??
  • ??
  • ?...?...????
  • epoll的高效就在于,當(dāng)我們調(diào)用epoll_ctl往里塞入百萬個句柄時,epoll_wait仍然可以飛快的返回,并有效的將發(fā)生事件的句柄給我們用戶。這是由于我們在調(diào)用epoll_create時,內(nèi)核除了幫我們在epoll文件系統(tǒng)里建了個file結(jié)點,在內(nèi)核cache里建了個紅黑樹用于存儲以后epoll_ctl傳來的socket外,還會再建立一個list鏈表,用于存儲準(zhǔn)備就緒的事件,當(dāng)epoll_wait調(diào)用時,僅僅觀察這個list鏈表里有沒有數(shù)據(jù)即可。有數(shù)據(jù)就返回,沒有數(shù)據(jù)就sleep,等到timeout時間到后即使鏈表沒數(shù)據(jù)也返回。所以,epoll_wait非常高效。

    而且,通常情況下即使我們要監(jiān)控百萬計的句柄,大多一次也只返回很少量的準(zhǔn)備就緒句柄而已,所以,epoll_wait僅需要從內(nèi)核態(tài)copy少量的句柄到用戶態(tài)而已,如何能不高效?!

    那么,這個準(zhǔn)備就緒list鏈表是怎么維護(hù)的呢?當(dāng)我們執(zhí)行epoll_ctl時,除了把socket放到epoll文件系統(tǒng)里file對象對應(yīng)的紅黑樹上之外,還會給內(nèi)核中斷處理程序注冊一個回調(diào)函數(shù),告訴內(nèi)核,如果這個句柄的中斷到了,就把它放到準(zhǔn)備就緒list鏈表里。所以,當(dāng)一個socket上有數(shù)據(jù)到了,內(nèi)核在把網(wǎng)卡上的數(shù)據(jù)copy到內(nèi)核中后就來把socket插入到準(zhǔn)備就緒鏈表里了。

    如此,一顆紅黑樹,一張準(zhǔn)備就緒句柄鏈表,少量的內(nèi)核cache,就幫我們解決了大并發(fā)下的socket處理問題。執(zhí)行epoll_create時,創(chuàng)建了紅黑樹和就緒鏈表,執(zhí)行epoll_ctl時,如果增加socket句柄,則檢查在紅黑樹中是否存在,存在立即返回,不存在則添加到樹干上,然后向內(nèi)核注冊回調(diào)函數(shù),用于當(dāng)中斷事件來臨時向準(zhǔn)備就緒鏈表中插入數(shù)據(jù)。執(zhí)行epoll_wait時立刻返回準(zhǔn)備就緒鏈表里的數(shù)據(jù)即可。

    最后看看epoll獨(dú)有的兩種模式LT和ET。無論是LT和ET模式,都適用于以上所說的流程。區(qū)別是,LT模式下,只要一個句柄上的事件一次沒有處理完,會在以后調(diào)用epoll_wait時次次返回這個句柄,而ET模式僅在第一次返回。

    這件事怎么做到的呢?當(dāng)一個socket句柄上有事件時,內(nèi)核會把該句柄插入上面所說的準(zhǔn)備就緒list鏈表,這時我們調(diào)用epoll_wait,會把準(zhǔn)備就緒的socket拷貝到用戶態(tài)內(nèi)存,然后清空準(zhǔn)備就緒list鏈表,最后,epoll_wait干了件事,就是檢查這些socket,如果不是ET模式(就是LT模式的句柄了),并且這些socket上確實有未處理的事件時,又把該句柄放回到剛剛清空的準(zhǔn)備就緒鏈表了。所以,非ET的句柄,只要它上面還有事件,epoll_wait每次都會返回。而ET模式的句柄,除非有新中斷到,即使socket上的事件沒有處理完,也是不會次次從epoll_wait返回的。

    總結(jié)

    以上是生活随笔為你收集整理的Linux下epoll如何实现高效处理百万句柄的的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。