epoll移植到windows的可行性研究

1、各有千秋

linux作爲高效穩定的操作系統,部署在大量的服務器上。epoll在linux下,一個高性能的網絡IO模型,在服務端領域發揮着重要的作用。但在開發效率上,windows以及visual studio系列因其良好的用戶體驗而更受用戶青睞。如果能夠將linux的運行期優勢和windows開發期的優勢,有機地結合在一起,是一個不錯的主義。

2、模型差異

和epoll相對應的是網絡IO模型是windows下的IOCP模型,但兩者之間存在一個很大的差別在於,epoll是在當資源可用時,系統觸發事件,應用層在執行操作;而iocp是由應用層預先分配操作資源,在操作完成後,系統再觸發事件。在操作和事件觸發的時間順序上正好相反。

除此之外,在接口設計上也諸多不同。雖然windows也提供了諸如socket、accept、send、recv之類的函數。可是,因爲epoll和iocp設計上的不同,不但不能直接使用,反而變成了epoll移植到windows上的障礙。

3、accept的移植

異步accept在windows下對應的是AcceptEx函數,這個函數會預先分配一個socket,當真實連接請求進入時,再使用setsockopt和GetAcceptExSockaddr獲取對應的信息。於是,我們可以將第一步AcceptEx作爲一個事件註冊的動作,隱藏在系統內部。當事件觸發後,在accept時,就執行後續的連接信息獲取動作,就可以和posix c的accept動作對應起來。

4、recv的移植

在正常模式下,應用層需要向IOCP註冊一個讀取緩衝區,在讀取完成後,IOCP觸發一個讀取完成事件。比較幸運的是,如果應用層註冊的是一個零字節的緩衝區,那麼在緩衝區可讀時,IOCP也會立即返回一個事件。於是,事件和操作的順序,就可以和epoll一致了。

事情依然沒有結束,當recv執行讀取操作時,如果返回WSA_IO_PENDING時,那麼操作就沒有完成,函數就不能返回。這時候,只能進入阻塞狀態,等待讀取完成。那麼總體的性能就會因此而降低。但同樣的是,windows爲我們提供一個ioctsocket的FIONREAD命令接口,讓我們可以偷看讀緩衝區中,究竟有多少個字節已經就緒,這樣就可以避免陷入WSA_IO_PENDING的狀態。

5、send的移植

send遇到的問題,和recv是一樣的。也可以通過註冊一個零字節的寫緩衝區,在IOCP觸發可寫事件後,再執行寫操作,從而保證和epoll的順序相同。

但不幸的是,send可以寫多少個字節而不會觸發WSA_IO_PENDING,windows卻沒有提供對應的接口。這個問題其實很容易理解,send的可寫字節是由連接上的收發雙方共同決定的,注意,是雙方,而不是單純地由可寫緩衝區單獨決定。那麼,這個事情就會變得複雜。

簡單起見,我們只能在內部管理一個緩衝區,將外部的發送請求數據先行拷貝到這個緩衝區,再由這個緩衝區向IOCP發送數據。通過這個緩衝區,我們就可以控制知道多少個字節可寫,即使觸發WSA_IO_PENDING,應用層也無需知道,也不會造成內部線程阻塞。但是性能也會因爲拷貝而損失。

6、接口移植

windows提供了和posix c一樣的socket類函數,因此如果我們改造 後的socket函數顯然無法和winsock函數共存,但是又不能不用winsock函數。因此,最好的辦法是通過動態加載ws2_32.dll,並導入到函數指針中,這個函數指針可以與原始函數名不同。在我們提供的函數中,仍然可以使用原名,同時,在這些函數中,插入我們而外增加的代碼。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章