libev源码解析——I/O模型
? ? ? ? 在《libev源碼解析——總覽》一文中,我們介紹過,libev是一個基于事件的循環庫。本文將介紹其和事件及循環之間的關系。(轉載請指明出于breaksoftware的csdn博客)
? ? ? ? 目前ibev支持如下IO事件模型:
- select模型。對應文件是ev_select.c。
- poll模型。對應文件是ev_poll.c。
- epoll模型。對應的文件是ev_epoll.c。
- port模型。對應文件是ev_port.c。
- kqueue模型。對應的文件是ev_kqueue.c。
- iocp模型。即IO完成端口模型(I/O Completion Port)。
? ? ? ? 這些模型并不是我們這個系列介紹的重點。如果想了解select、poll、epoll模型,可以參閱《樸素、Select、Poll和Epoll網絡編程模型實現和分析》系列博文。(下圖是select模型的調用邏輯圖)
? ? ? ??
? ? ? ? 此處我們只要知道它們是libev可選的事件模型即可。至于選擇什么模型。要視loop_init的入參flags。
static void noinline ecb_cold
loop_init (EV_P_ unsigned int flags) EV_THROW
{
……
#if EV_USE_IOCPif (!backend && (flags & EVBACKEND_IOCP )) backend = iocp_init (EV_A_ flags);
#endif
#if EV_USE_PORTif (!backend && (flags & EVBACKEND_PORT )) backend = port_init (EV_A_ flags);
#endif
#if EV_USE_KQUEUEif (!backend && (flags & EVBACKEND_KQUEUE)) backend = kqueue_init (EV_A_ flags);
#endif
#if EV_USE_EPOLLif (!backend && (flags & EVBACKEND_EPOLL )) backend = epoll_init (EV_A_ flags);
#endif
#if EV_USE_POLLif (!backend && (flags & EVBACKEND_POLL )) backend = poll_init (EV_A_ flags);
#endif
#if EV_USE_SELECTif (!backend && (flags & EVBACKEND_SELECT)) backend = select_init (EV_A_ flags);
#endif
……
}
? ? ? ? backend是一個用于記錄libev使用的是哪種IO模型的標記位。
? ? ? ? 在每個模型初始化函數中,都需要指定兩個模型相關的函數指針。比如epoll模型的初始化函數epoll_init中
int inline_size
epoll_init (EV_P_ int flags)
{
……backend_mintime = 1e-3; /* epoll does sometimes return early, this is just to avoid the worst */backend_modify = epoll_modify;backend_poll = epoll_poll;
……
}
? ? ? ? 而在select模型中則是
int inline_size
select_init (EV_P_ int flags)
{backend_mintime = 1e-6;backend_modify = select_modify;backend_poll = select_poll;
……
}
? ? ? ? backend_mintime是需要等待事件的超時秒數;backend_modify是輪詢中修改事件信息的函數。backend_poll則是等待事件的函數。libev通過上述四個變量,隔離了不同模型選擇導致不同函數調用的問題。
? ? ? ? 但是這兒需要指出的是,libev并沒有將這種隔離做徹底。因為在關閉IO模型時,它仍然依靠backend的值,調用了不同函數(ev_loop_destroy中)
#if EV_USE_IOCPif (backend == EVBACKEND_IOCP ) iocp_destroy (EV_A);
#endif
#if EV_USE_PORTif (backend == EVBACKEND_PORT ) port_destroy (EV_A);
#endif
#if EV_USE_KQUEUEif (backend == EVBACKEND_KQUEUE) kqueue_destroy (EV_A);
#endif
#if EV_USE_EPOLLif (backend == EVBACKEND_EPOLL ) epoll_destroy (EV_A);
#endif
#if EV_USE_POLLif (backend == EVBACKEND_POLL ) poll_destroy (EV_A);
#endif
#if EV_USE_SELECTif (backend == EVBACKEND_SELECT) select_destroy (EV_A);
#endif
? ? ? ? 個人認為,可以在各個模型的初始化中,將其對應的銷毀函數指針賦值給一個叫backend_destory的變量。這樣上述代表就可以變成一行了。
? ? ? ? 結合《libev源碼解析——調度策略》的內容,我們可以用下圖表達出libev運轉的大體流程。
? ? ? ? 針對上圖,可能有人會問:為什么backend_poll函數需要指定超時?我們讓其一直等待到有事件發生不是更好么?
? ? ? ? 還有人會問:“符合條件的監視器”是否可以表述為“本次觸發事件對應的監視器”?
? ? ? ? 對于這些問題,我們將在之后章節給出答案。
總結
以上是生活随笔為你收集整理的libev源码解析——I/O模型的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: libev源码解析——调度策略
- 下一篇: libev源码解析——定时器原理