IO多路转接模型2

Select:
操作流程:
1.定义事件的描述符集合(位图),清空集合,添加需要监控的描述符
2.将集合拷贝到内核中进行监控,有就绪/超时则监控返回,返回前将未就绪的描述符从集合中移除
3.监控调用返回后,获取到就绪的描述符集合,判断哪个描述符还在哪个集合中确定这个描述符是否就绪了某个事件
接口:fd_set/FD_ZERO/FD_SET/FD_ISSET/FD_CLR/select
优缺点:
优点:跨平台移植性良好
缺点:1.select所能监控的描述符有最大数量上限
2.每次监控都需要重新向集合中添加描述符,并且拷贝到内核
3.select监控原理遍历轮询,性能会随着描述符增多而下降
4.select无法直接提供就绪描述符,需要监控进行遍历

Poll:
接口认识:
Int poll(struct pollfd *fds,nfds_t fds,int to);
Struct pollfd{
int fd;//要监控的描述符
short events;//fd描述符要监控的事件POLLIN-读/POLLOUT-写
short revents;//监控调用返回后,用于记录实际就绪的事件
}
fds:描述符事件结构数组 nfds:数组中有效节点个数 timeout:超时时间
返回值:等于0表示超时 小于0表示出错 有就绪则大于0
操作流程:
1.定义事件结构体数组,为每个需要监控的描述符定义事件结构
2.发起监控调用,将数组中有效节点拷贝到内核进行监控,超时/就绪则调用返回,返回前将描述符实际就绪的事件
记录到对应节点的revents成员中
3.调用返回后,遍历事件数组,通过每个结点的revents成员确定对应节点描述符是否就绪了某个事件
优缺点:
优点:
1.poll能够监控的描述符数量没有上限限制
2.代码操作流程相较于select较为简单
缺点:
1.跨平台移植性较差
2.监控原理依然是遍历轮询,性能会随着描述符增多而下降
3.监控返回后依然需要遍历事件结构数组确定描述符是否就绪

epoll:linux下最好用的多路转接模型
接口:
1.int epoll_create(int size);—在内核中创建epoll句柄
size:监控的数量上限 ,Linux2.6之后被忽略,大于0即可
返回值:成功返回描述符,失败返回-1;
2.int epoll_ctl(int epfd, int op, int fd ,struct epoll_event *ev);
epfd:epoll_create返回的epoll描述符;
op:EPOLL_CTL_ADD/EPOLL_CTL_DEL/EPOLL_CTL_MOD
fd:针对op类型所操作的监控描述符
ev:针对fd描述符所定义的事件结构体
struct epoll_event{
uint32_t events;//要监控的事件-监控返回后实际就绪事件
epoll_data_t{void *ptr;int fd;}data;
}
返回值:成功返回0,失败返回-1
EPOLLIN /EPOLLOUT/EPOLLET
3.int epoll_wait(int epfd,struct epoll_event *evs, int maxe, int to)
epfd:epoll描述符;
evs:epoll_event数组 首地址,用于保存就绪的描述符对应事件结构
maxe:通常设定为evs数组的节点个数–指定要获取的事件最大个数
to:超时时间-毫秒
返回值:超时返回0;出错返回-1,有就绪返回就绪事件个数
操作流程:
1.在内核中创建epoll句柄结构
struct eventpoll{…rdllist–双向链表,rbr–红黑树;…}
rbr:用于保存监控的描述符节点
rdllist:用于保存就绪的描述符对应事件结构
2.向内核epoll 句柄结构中添加要监控的描述符以及对应事件结构
3.传入一个事件结构数组,开始监控,监控是一个异步阻塞操作
告诉系统开始监控,而描述符的监控由系统完成系统为每个描述符
的就绪事件做了一个事件回调函数,一旦摸个描述符就绪了指定的
事件,则会调用事件回调函数,将这个描述符对应的事件结构添加到就绪事件双向链表
3.epoll_wait每隔一段时间查看epoll句柄结构的rdllist-就绪双向链表是否为空,就可以判断有没有描述符就绪,
超时则直接返回,如果有就绪,则将就绪的事件结构信息拷贝到传入的数组中
4.监控调用
epoll的事件触发方式
IO事件的就绪:
可写:描述符的发送缓冲区中剩余空间大小大于低水位标记
可读:描述符的接收缓冲区中数据大小大于低水位标记
低水位标记:类似于一个基准值–默认一个字节
IO就绪事件的触发方式:水平触发-默认 边缘触发-epoll特有
水平触发:EPOLLLT-默认
可读:只要缓冲区中有数据就会触发可读事件
可写:只要缓冲区中有剩余空间就会触发可写事件
边缘触发:EPOLLET
可读:只有新数据到来的时候才会触发可读事件
可写:只有缓冲区从没有剩余空间变为有剩余才会触发可写事件
边缘触发可以提高任务处理效率:
假设有个描述符有数据来了,但是数据不完整,这种情况下使用水平触发,如果不取出数据就会一直触发事件,而使用
边缘触发则可以实现在有新数据到来的时候触发事件,查看数据是否完整(防止一种事件不断被触发,但是不太想去处理的场景)
边缘触发会导致的问题:
边缘触发只有在新数据到来的时候才会触发事件,意味着再一次事件触发中就必须将需要处理的数据完全取出处理,因为在没有
新数据到来的情况下不会再次触发事件去处理剩余的数据。
如何将缓冲区中所有数据全部取出?
因为不知道缓冲区中有多少数据,因此只能循环进行读取,直到取不出来数据为止,但是这样会出现一种情况–没有数据则继续读
就会阻塞 因此边缘触发必须使用非阻塞操作,循环读取到缓冲区中没有数据的时候就会报错返回。errno=EAGAIN
int fcnt(int fd,int cmd,…/arg/)
cmd:F_GETFL获取描述符属性通过返回值返回,arg被忽略
F_SETFL设置描述符属性 arg=O_NONBLOCK
int flag=fcntl(fd,F_GETFL,0);
fcntl(fd,F_SETFL,flag|O_NONBLOCK);
epoll优缺点:
缺点:跨平台移植性较差
优点:
1.所能监控的描述符没有数量上限
2.描述符以及事件结构只需要向内核拷贝一次
3.监控原理采用异步阻塞操作,监控由系统完成,进程只需要判断就绪链表是否为NULL即可,性能不会随着描述符增多而下降
4.直接返回就绪的描述符对应事件结构,减少空遍历。
多路转接模型:
适用于有大量描述符需要监控,但是同一时间只有少量活跃的场景
poll/select适用于单个描述符的超时控制
单个描述符的临时超时控制这里不适用epoll
在实际使用中,多路转接模型通常搭配线程池一起使用
对大量描述符进行监控,就绪事件后则抛入线程池进行处理

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值