基本概念
- IO對象:文件、管道、磁盤、socket ...
- IO操作:就是一次讀或者寫的請求
- Windows: ReadFile、WriteFile、WSASend、WSARecv、AcceptEx ...
- Linux: read、write、send、recv、accept ...
EPOLL
- epoll_create 創建一個epoll對象(在linux里對象一般都是通過文件描述符來訪問的,文件描述符更符合linux的術語,下面統一叫FD(File Descriptor)吧)
- epoll_ctl 將IO對象關聯到這個epoll對象
- epoll_wait 獲取發生IO事件的IO對象
epoll_event,里面包含了事件的類型(.events),還有一個聯合體data,這個data里你可以直接放IO對象的fd(.data.fd),或者放一個你自己的指針(.data.ptr),他的作用就是 讓你能夠找到發生事件的IO對象
IOCP
使用IOCP你要搞明白兩個結構
- OVERLAPPED
OVERLAPPED的英文含義是重疊,在編程中意義就是你可以在一個異步IO未完成之前,再進行一次IO操作。這個結構和每一次讀寫操作有關,就是你每調用一次IO函數,都要傳一個這樣的結構。一般的流程是:調用一個可以異步的IO函數,傳入一個OVERLAPPED結構體指針,等這個IO的數據到達/送到了,系統再將這個OVERLAPPED指針還給你, 這個結構是和每一次IO操作相關的 - complete_key
這個其實不是結構體,只是GetQueuedCompletionStatus函數的一個指針類型的參數,他的作用是讓你找到發生事件的IO對象,和epoll_event
里的.data.ptr的作用是一樣的
總結
這兩種機制都是通過將IO對象關聯到一個 統一管理IO事件的對象 (Windows: CompletePortQueue, Linux:epoll-fd) 中來實現異步操作,不同的是:
- Windows的IOCP除了要將IO對象關聯到IOCP對象之外,還需要調用一個異步的IO函數並傳入OVERLAPPED結構來告訴IOCP你要進行IO,這相當於你告訴IOCP你這次要監聽哪種事件,而且事件每發生一次,你還得再調用一次異步IO函數再告訴IOCP一次。當事件發生時,IOCP機制會將數據直接拷貝到和OVERLAPPED結構關聯的緩沖區里,你直接用就可以了
- linux的epoll只需要將IO對象關聯到epoll對象,並設置好關心的事件類型,當事件發生的時候才去調用IO函數獲取數據,然后再使用數據
EPOLL看起來更像是 異步通知、IOCP看起來像是 異步傳輸
在IOCP里,識別IO事件是通過調用具體的IO函數並傳入自定義的OVERLAPPED結構來實現的,在epoll里統一通過epoll_event的events字段指定。可以看出,linux的epoll模型在使用上更簡單一些
使用異步的IO接口可以 用一個獲少量的線程接管所有IO對象的事件,這樣就避免了多線程同步模型里對那些沒有IO事件發生的對象無意義的等待,大大提高了CPU利用率和網絡吞吐率