IOCP vs EPOLL
基本概念
- 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利用率和网络吞吐率