三种IO多路转接模型来实现多路转接IO:
select:
int select(int nfd,fd_set *rfds,fd_set *wfds,fd_set *efds,struct timeval *tv)
FD_ZERO(fd_set *set) FD_ISSET(int fd,fd_set *set)
FD_SET(int fd,fd_set *set) FD_CLR(int fd,fd_set *set)
1.用户自己定义描述符事件监控集合,将要监控的描述符添加到指定的集合中
2.将事件集合拷贝到内核中
3.在内核中进行轮询遍历监控集合中的描述符是否发生了用户所关心的事件
a.没有描述符发生事件,挂起等待,隔一段时间重新遍历,若超时则直接调用返回
b.若有描述符发生事件,从集合中将没有就绪的描述符移除,调用返回
4.调用返回后,返回给用户一个就绪的描述符集合,需要用户进行遍历,判断哪些描述符在集合中进行
找到就绪的描述符,进而对描述符进行相应操作。
缺点分析:
1.select所能监控的描述符是有上限的,上限取决于_FD_SETSIZE=1024
2.select每次都需要将集合拷贝到内核中进行监控,涉及到用户态与内核态之间的数据拷贝过程
3.select在内核中进行描述符监控,采用轮询遍历,性能会随着描述符增多而下降
4.select在调用返回时会从集合中移除未就绪描述符,需要用户每次监控时重新添加描述符到集合
5.select只给进程返回就绪的描述符集合,需要用户遍历查找就绪的描述符
优点分析:
1.select遵循posix标准,可以跨平台
poll:
int poll(struct pollfd *fds,int maxfds,int timeout)
struct pollfd {
int fd; 用户所要监控的描述符
uint32_t events; 用户对监控的描述符所关心的事件 POLLIN_POLLOUT
uint32_t revents; 描述符实际就绪的事件
}
1.用户为每一个描述符定义事件结构,赋值描述符以及关心的事件
2.将描述符事件结构拷贝到内核进行监控
3.在内核中采用轮询遍历的监控方法,性能会随着描述符增多而下降
a.当没有描述符就绪,挂起进程,隔一会继续轮询遍历,若超时则调用返回
b.当有描述符就绪,将就绪的事件添加到事件结构的revents中,调用返回
4.需要用遍历事件数组,查看事件结构中的revents中的事件信息,来找到就绪的描述符,进而对其操作
优点分析:
1.每次监控都需要将事件结构拷贝到内核进行监控
2.在内核中监控采用轮询遍历,性能会随着描述符增多而下降
3.返回调用后,需要用户遍历事件数组,判断revents才能找到就绪的描述符
4.不能跨平台
缺点分析:
1.采用事件结构对描述符进行事件监控,简化了多个监控集合的操作流程
2.poll没有监控描述符的最大数量上限,取决于硬件资源
epoll:
int epoll_create(int maxfd) ——在内核中创建eventpoll结构,并且返回文件描述符作为操作句柄
int epoll_ctl(int epfd,int op,int fd,struct epoll_event &ev)
int epoll_wait(int epfd,struct epoll_event *evs,int maxfds,int timeout)
1.通过epoll_create创建eventpoll结构,返回操作句柄
2.通过操作句柄向内核中添加描述符的监控事件epoll_ctl
3.通过epoll_wait开始监控,异步阻塞操作,描述符的监控由操作系统完成
a.每隔一段时间,则查看eventpoll中双向链表是否为空,判断是否有描述符就绪
若没有就绪,则挂起等待,隔一会继续等待,若超时则返回
4.若有就绪返回时,epoll_wait将就绪的事件信息放置到事件数组evs中,返回给用户
5.用户通过epoll_wait传入的evs获取到就绪的事件,直接遍历操作
优点分析:
1.采用事件结构对描述符进行监控,简化了多个监控集合的操作流程
2.没有描述符的最大数量限制
3.描述符的事件信息,只需要向内核拷贝一次
4.在内核中事件监控采用事件回调的方法,不需要遍历描述符,只需要判断就绪链表是否为空,性能
不会随着描述符增多而下降
5.直接返回就绪的事件节点,用户可以直接对就绪事件进行操作,不需要进行空遍历
缺点:
1.不能跨平台
epoll的事件触发方式:
水平触发方式:
可读事件:socket缓冲区中只要数据大小大于低水位标记则会触发就绪事件
可写事件:socket缓冲区中只要空闲空间大小大于低水位标记则会触发就绪事件
边缘触发方式:
可读事件:socket缓冲区中只有新数据到来时才会触发一次事件
可写事件:socket缓冲区中剩余空间大小只有从无到有的时候才会触发一次事件
可读事件边缘触发方式中:
当有新数据到来,事件就绪时,需要用户一次将数据全部读取完毕,因为若数据没有完全读取,
剩下的数据不会触发就绪事件,这时候进程不会对非就绪描述符进行操作,这个描述符剩下的数据
只有等待下次新数据到来的时候才能处理剩下的数据。
但是一次将所有数据读取完,需要循环读取,循环读取当缓冲区没有数据的时候就会阻塞,程序
流程在停止;因此需要将描述符设置为非阻塞。