IO多路复用:select, poll , epoll

IO多路转接模型( IO复用 ):
就是针对描述符进行IO就绪监控,防止系统对IO未就绪的时间进行操作,避免因为IO未就绪导致的阻塞,提高了效率。

通常应用于TCP服务器端,针对大量套接字描述符进行监控,让程序能仅针对就绪的描述符进行操作,进而提高处理效率。
而udp服务端大多针对单个套接字进行操作,大多数情况也会用到多路转接模型,因为多路转接不但可以进行IO就绪时间监控,而且还可以进行超时控制。

IO多路转接模型分为 :select poll epoll

select:
监控原理就是:
1.创建一个描述符集合,清空集合。
2.像描述符中添加要监控的描述符。
3.将这个集合拷贝到内核中进行select监控。
4.监控完毕后返回一个就绪集合。注意返回之前会将这个集合中的未就绪的描述符移除。
5.遍历这个就绪集合,就可以知道这个描述符就绪了什么事件。

接口介绍:
1.创建描述符集合
fd_set rfds;
2.清空描述符集合
void FD_ZERO(fd_set *set);
3.向集合中添加描述符
void FD_SET(int fd, fd_set *set);
4.将集合拷贝到内核中进行监控
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
5.移除标识符
void FD_CLR(int fd, fd_set *set);
6.判断描述符是否在集合中
int FD_ISSET(int fd, fd_set *set);

优缺点:

优点:
select可以跨平台移植,而poll和epoll无法跨平台移植

缺点:
1.每次监控完成之后都需要重新向集合中添加描述符,并且拷贝到内核

2.监控描述符数量有最大限制,默认为1024个

3.监控原理是在内核中进行轮询便利,性能会随着描述符的增多而下降
在描述符中遍历一遍看是否有就绪的。
如果有描述符就绪则返回,没有描述符就绪则挂起等待。
. 超时或者有描述符就绪则唤醒,重新遍历移除未就绪后返回。

4.select监控完返回的就序集合,需要用户自己判断那个描述符还在哪个集合中,才能确定哪个描述符就绪了哪个事件。

poll:
操作接口
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
第一个参数为一个存放了元素为pollfd结构体的数组。结构体中有三个变量分别为
fd:要监控的描述符 events:fd描述符要监控的事件 revents:监控返回后,用于记录实际就绪的事件
events:POLLIN读/POLLOUT写
第二个参数为 有效节点数量,
第三个参数为超时时间。
在这里插入图片描述
操作流程就是
1.定义一个事件结构体数组,为每一个需要监控的描述符定义结构体结构。
2.发起监控调用,将数组中有效节点拷贝到内核中进行监控,超市或者有描述符节点就绪调用返回,返回前将描述符实际就绪的事件记录到对应节点的revents中。
3.遍历事件数组,通过每个节点的revents成员确定这个描述符是否就绪了某个事件。

优缺点:
优点:
1.poll能够监控的描述符没有上限限制
2.代码操作流程相较于select较为简单

缺点:
1.跨平台移植性差
2.监控原理依然是遍历轮询,性能会随着描述符增多而下降。
3.监控返回后依然需要遍历事件结构数组确定描述符是否就绪

epoll:linux下最好用的多路转接模型
接口认识:
1.在内核中创建一个epoll句柄。
int epoll_create(int size);
2.向这个epoll句柄里面添加要监控的描述符。
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
epfd:就是epoll_create返回的操作句柄。
op:控制选项
EPOLL_ADD 添加描述符 EPOLL_DEL删除描述符 EPOLL_MOD 修改描述符
fd:要监控的描述符
event:
在这里插入图片描述

3.开始监控
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
epfd:是epoll_create 返回的操作句柄
events:返回实际就绪的事件
maxevents—设定为evs数组的节点个数–指定要获取的时间最大个数
timeout:超时时间

操作流程:
1.在内核中创建epoll句柄结构
struct eventpoll{。。。。rdllist–双向链表,rbr–红黑树。。。}
rbr:用于保存要监控的描述符节点
rdllist:用于保存就绪的描述符对应事件结构
2.向内核epoll句柄结构中添加要监控的描述符以及对应事件结构
3.传入一个事件结构数组,开始监控,监控是一个异步阻塞操作
告诉系统开始监控,而描述符的监控由系统完成
系统为每个描述符的就绪事件做了一个事件回调函数,一旦某个描述符就绪了指定的事件,则会调用事件回调函数,将这个描述符对应的事件结构添加到就绪事件双向链表。
epoll_wait接口每隔一段时间查看epoll句柄结构的rdllist–就绪双向链表是否为空,就可以判断有没有描述符就绪,超时则直接返回,如果有就绪,则就绪的事件结构信息拷贝到传入的数组中。
4.监控调用返回后,只需要遍历events数组,逐个对节点中的描述符进行对应事件的处理即可。

epoll优缺点
缺点
跨平台移植性差
优点
1.所能监控的描述符没有数据上限
2.不用每次都重新添加节点
3.监控远离采用的是异步阻塞,监控由系统完成,进程只需要判断链表中是否为null即可,
性能不会随着描述符的增多而下降
4.直接返回的都是就绪的描述符对应结构,减少了空便利。


多路转接模型都适用于大量描述符需要监控,但是同一时间只有少量活跃的场景
poll/select适用于单个描述符的超时控制
单个描述符的临时超时控制不适用于epoll
在实际使用中,多路转接模型通常搭配线程池一起使用
对大量描述符进行监控,就绪事件后则抛入线程池进行处理

水平触发(默认的)
可读:只要缓冲区有数据就会触发可读事件
可写:只要缓冲区有剩余空间就会触发可写事件
边缘触发:
可读:只有有新数据到来才会触发可读事件
可写:只有缓冲区没有数据了才会触发可写事件

边缘触发可以提高任务处理数据(防止一种时间不断被触发,但是不太想去处理的场景)
边缘触发只有在新数据到来的时候才会触发事件,意味着再一次事件触发中就必须将需要处理的数据完全取出处理,因为在没有新数据到来的情况下不会再次出发事件去处理剩余的数据。

那我们如何将缓冲区中所有数据全部取出呢?

因为不知道缓冲区中有多少数据,因此只能循环进行读取,直到取不出数据为止,但是这样会出现一种情况—没有数据则继续读就会阻塞。因此边缘触发的O必须使用非阻塞操作,循环读取到缓冲区中没有数据时就会报错返回。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值