linux下epoll如何實現高效處理百萬控制程式碼的
開 發高效能網路程式時,windows開發者們言必稱iocp,linux開發者們則言必稱epoll。大家都明白epoll是一種IO多路複用技術,可以 非常高效的處理數以百萬計的socket控制程式碼,比起以前的select和poll效率高大發了。我們用起epoll來都感覺挺爽,確實快,那麼,它到底為 什麼可以高速處理這麼多併發連線呢?
先簡單回顧下如何使用C庫封裝的3個epoll系統呼叫吧。
- 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);
使用起來很清晰,首先要呼叫epoll_create建立一個epoll物件。引數size是核心保證能夠正確處理的最大控制程式碼數,多於這個最大數時核心可不保證效果。
epoll_ctl可以操作上面建立的epoll,例如,將剛建立的socket加入到epoll中讓其監控,或者把 epoll正在監控的某個socket控制程式碼移出epoll,不再監控它等等。
epoll_wait在呼叫時,在給定的timeout時間內,當在監控的所有控制程式碼中有事件發生時,就返回使用者態的程式。
從 上面的呼叫方式就可以看到epoll比select/poll的優越之處:因為後者每次呼叫時都要傳遞你所要監控的所有socket給select /poll系統呼叫,這意味著需要將使用者態的socket列表copy到核心態,如果以萬計的控制程式碼會導致每次都要copy幾十幾百KB的記憶體到核心態,非 常低效。而我們呼叫epoll_wait時就相當於以往呼叫select/poll,但是這時卻不用傳遞socket控制程式碼給核心,因為核心已經在 epoll_ctl中拿到了要監控的控制程式碼列表。
所以,實際上在你呼叫epoll_create後,核心就已經在核心態開始準備幫你儲存要監控的控制程式碼了,每次呼叫epoll_ctl只是在往核心的資料結構裡塞入新的socket控制程式碼。
在核心裡,一切皆檔案。所以,epoll向核心註冊了一個檔案系統,用於儲存上述的被監控socket。當你呼叫epoll_create時,就會在這個虛擬的epoll檔案系統裡建立一個file結點。當然這個file不是普通檔案,它只服務於epoll。
epoll 在被核心初始化時(作業系統啟動),同時會開闢出epoll自己的核心高速cache區,用於安置每一個我們想監控的socket,這些socket會以 紅黑樹的形式儲存在核心cache裡,以支援快速的查詢、插入、刪除。這個核心高速cache區,就是建立連續的實體記憶體頁,然後在之上建立slab層, 簡單的說,就是物理上分配好你想要的size的記憶體物件,每次使用時都是使用空閒的已分配好的物件。
- 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 的高效就在於,當我們呼叫epoll_ctl往裡塞入百萬個控制程式碼時,epoll_wait仍然可以飛快的返回,並有效的將發生事件的控制程式碼給我們使用者。這是 由於我們在呼叫epoll_create時,核心除了幫我們在epoll檔案系統裡建了個file結點,在核心cache裡建了個紅黑樹用於儲存以後 epoll_ctl傳來的socket外,還會再建立一個list連結串列,用於儲存準備就緒的事件,當epoll_wait呼叫時,僅僅觀察這個list鏈 表裡有沒有資料即可。有資料就返回,沒有資料就sleep,等到timeout時間到後即使連結串列沒資料也返回。所以,epoll_wait非常高效。
而且,通常情況下即使我們要監控百萬計的控制程式碼,大多一次也只返回很少量的準備就緒控制程式碼而已,所以,epoll_wait僅需要從核心態copy少量的控制程式碼到使用者態而已,如何能不高效?!
那 麼,這個準備就緒list連結串列是怎麼維護的呢?當我們執行epoll_ctl時,除了把socket放到epoll檔案系統裡file物件對應的紅黑樹上 之外,還會給核心中斷處理程式註冊一個回撥函式,告訴核心,如果這個控制程式碼的中斷到了,就把它放到準備就緒list連結串列裡。所以,當一個socket上有數 據到了,核心在把網路卡上的資料copy到核心中後就來把socket插入到準備就緒連結串列裡了。
如 此,一顆紅黑樹,一張準備就緒控制程式碼連結串列,少量的核心cache,就幫我們解決了大併發下的socket處理問題。執行epoll_create時,建立了 紅黑樹和就緒連結串列,執行epoll_ctl時,如果增加socket控制程式碼,則檢查在紅黑樹中是否存在,存在立即返回,不存在則新增到樹幹上,然後向核心注 冊回撥函式,用於當中斷事件來臨時向準備就緒連結串列中插入資料。執行epoll_wait時立刻返回準備就緒連結串列裡的資料即可。
最後看看epoll獨有的兩種模式LT和ET。無論是LT和ET模式,都適用於以上所說的流程。區別是,LT模式下,只要一個控制程式碼上的事件一次沒有處理完,會在以後呼叫epoll_wait時次次返回這個控制程式碼,而ET模式僅在第一次返回。
這 件事怎麼做到的呢?當一個socket控制程式碼上有事件時,核心會把該控制程式碼插入上面所說的準備就緒list連結串列,這時我們呼叫epoll_wait,會把準備 就緒的socket拷貝到使用者態記憶體,然後清空準備就緒list連結串列,最後,epoll_wait幹了件事,就是檢查這些socket,如果不是ET模式 (就是LT模式的控制程式碼了),並且這些socket上確實有未處理的事件時,又把該控制程式碼放回到剛剛清空的準備就緒連結串列了。所以,非ET的控制程式碼,只要它上面還 有事件,epoll_wait每次都會返回。而ET模式的控制程式碼,除非有新中斷到,即使socket上的事件沒有處理完,也是不會次次從 epoll_wait返回的。
相關文章
- Linux程式卡死,如何處理?Linux
- java大資料處理:如何使用Java技術實現高效的大資料處理Java大資料
- linux下gdb如何處理coredump錯誤Linux
- linux系統下poll和epoll核心原始碼剖析Linux原始碼
- 從linux原始碼看epollLinux原始碼
- 如何高效的處理陣列對映陣列
- 影片直播系統原始碼,非同步處理實現程式碼分析原始碼非同步
- epoll實現快速ping
- epoll程式設計,單epoll+執行緒池?執行緒池+epoll?nginx實現高併發的原理?程式設計執行緒Nginx
- 有限資源下如何實現最高效的資料處理?四個“智慧城市”專案尋找“最優解”
- epoll+socket實現 socket併發 linux伺服器Linux伺服器
- 如何簡單高效的在程式碼中實現兩級快取的管理快取
- 如何利用Redis實現延時處理Redis
- 實用處理字串的linux命令字串Linux
- 手把手教你如何寫事件處理的程式碼事件
- Java網路程式設計和NIO詳解6:Linux epoll實現原理詳解Java程式設計Linux
- Linux下使用supervisor實現程式管理Linux
- LINUX程式如何管理控制(二)Linux
- 如何實現一個高效的本地日誌收集程式
- 小程式程式碼打包處理
- 如何用 Golang 的 channel 實現訊息的批量處理Golang
- 如何用 Golang 的 channel 實現訊息的批次處理Golang
- 避免 OOM,高效匯出百萬級資料的 SpringBoot 實現方案OOMSpring Boot
- Linux下使用Ansible處理批量操作Linux
- 如何在不使用POJO程式碼的情況下建立Java批處理插入 -DZone JavaPOJOJava
- 77 行程式碼實現 ABAP ALV 中的雙擊事件處理試讀版行程事件
- 如何實現程式碼高亮
- 如何讓 python 處理速度翻倍?內含程式碼Python
- 高效實用的照片批次處理軟體:PhotoBulk 中文
- 如何編寫高效的Android程式碼Android
- Linux 和 Windows 下編碼問題處理 codestyle 解決方法LinuxWindows
- Linux系統程式設計之訊號中斷處理(下)Linux程式設計
- Linux 使用 shell 指令碼處理字串Linux指令碼字串
- 資料分析處理之PCA OLSR PCR PLSR(NIPALS)及其Matlab程式碼實現PCAMatlab
- Kafka如何實現實時流處理 Part 1 - André MeloKafka
- 如何實現高效的團隊合作?
- 高手如何處理快取:SpringBoot整合Redis實現快取處理(AOP技術)!快取Spring BootRedis
- LINUX下完整的TCP epoll 伺服器和客戶端程式碼,用作備份LinuxTCP伺服器客戶端
- linux網路程式設計中的errno處理Linux程式設計