I/O多路转接select/poll/epoll模型

I/O多路转接(select/poll/epoll)

1、I/O多路转接select

系统提供select函数实现多路复用的输入输出模型

select系统调用让程序监视多个文件描述符的状态变化,程序会暂停到select这里等待,直到被监视的文件描述符发送了状态变化

#include<sys/select.h>  //头文件

int select (int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
struct timeval *timeout )

参数解释:
nfds:用来限定监视区间(最大文件描述符+1rdset: 检测读事件的文件描述符集合
wrset:检测写事件的文件描述符集合
exset:检测异常事件的文件描述符集合
timeout:设置select的等待时间

timeout取值

NULL:一直被阻塞直接文件描述发生变化
0:检测文件描述符状态立即返回
特定时间:等待时间

fd_set结构 //位图(取fd_set为一个字节,每一bit对应一个文件描述符fd)
提供操作fd_set的一组接口,来操作位图

void FD_CLR(int fd, fd_set *set)  //清除set中fd相关位
int  FD_ISSET(int fd, fd_set *set)  //检测set相关位
void FD_SET(int fd, fd_set *set)    //设置set相关位
void FD_ZERO(fd_set *set)         //清除set全部位

socket就绪条件

读就绪
1、 socket内核中,接收缓冲区字节数大于等于低标记,无阻塞读。
2 、 socket的TCP通信中,对端关闭连接,对socket,读返回0。
3 、 监听的socket中有新的连接请求。
4 、 socket上有未处理的错误。

写就绪
1、 socket内核中,发生缓冲区可用字节数大于等于低标记,无阻塞写。
2、 socket的写操作被关闭,对一个写操作被关闭的socket进行写操作出发管道(SIGPIPE)信号。
3、 socket使用非阻塞connect连接成功或失败之后。
4、 socket上有未读取的错误。

select的特点
可监控文件描述符的大小取决于sizeof(fdset)的值
将fd加入select监控集的同时,使用array[ ]保存select监控集中的fd
1用于select返回,arrary[]和fdset进行FD_ISSET判断

select的缺点

扫描二维码关注公众号,回复: 3310987 查看本文章

1、每次调用select都要手动设置fd集合,从接口使用角度来说也便(设置监听集合)
2、每次调用select都要把fd集合从用户态拷到内核态,开销比大(告诉内核我要监听那些数据)
3、每次调用select都需要在内核态遍历传进来的所有fd,开销大(检测有那些监听的数据发生了变化)
4、select支持文件描述符数量太小。

2、I/O多路转接poll

函数poll

#include<poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout)

参数说:
fds: poll函数监听的结构体 包含 1文件描述符,2监听事件集合,3返回事件合
nfds:数组长度
timeout:表示poll函数超时间间

socket就绪条件 (同select)

poll的优点

1、pollfd结构包含监视事件/发生事件,不再使用select“参数-值”传递方式(不需要重新设置和添加)
2、poll并没有最大数量限制(但是数量过大后性能也会下降)

poll的缺点

poll监听的文件描述符数目增多时
1、和select函数一样,poll返回后,需要轮询pollfd获取就绪的描述符
2、每次调用poll都需要把大量的pollfd结构从用户态拷贝到内核中
3、同时连接大量客户端某时刻可能只有少数的就绪状态,因此效率可能会下降

I/O多路转接epoll

epoll相关系统调用
1、epoll_create
创建epoll句柄,用完后必须调用close( )关闭

epoll_create
int epoll_create(int size)

2、epoll_ctl
epoll的事件注册函数
不同于之前select()是在监听事件时告诉内核要监听什么类型的事件,而这里是先注册事件的类型。

epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)

参数说明:
epfd: 是epoll_create()的返回值
op: 表示动作,用三个宏来表示(注册 / 修改/ 删除)
fd: 表示需要监听的fd
event: 告诉内核需要监听什么事件

3、epoll_wait
收集在epoll监控的事件中已经发生的事件

int epoll_wait(int epfd, struct epoll_event* events,
                             int maxevents, int timeout)

参数说明:
events: 取就绪事件的文件描述符信息赋值到events中 (数组)
maxevents: 告诉内核events数组有多大(不能超过创建时size的大小)
timeout:超时时间

返回结果
函数调用成功,返回准备好的文件描述符数目,返回0则超时,小于0失败。

epoll工作原理

这里写图片描述

epoll模型图
这里写图片描述

epoll的使用总结

1、调用epoll_create()创建一个epoll句柄
2、调用epoll_ctl()将监控的文件描述符进行注册
3、调用epoll_wait()等待文件描述符就绪

epoll的优点

1、文件描述符无上限:
通过epoll_ctl()注册一个文件描述符,内核使用红黑树来管理所有需要i就安康的文件描述符

2、基于事件的就绪通知方式:
一旦被监听的某个文件描述符就绪,内核采用回调机制激活文件描述符。

3、维护就绪队列:
当文件描述符就绪,会被放到内核中的一个就绪队列中,调用epoll_wait就绪文件描述符的时候,只要获取队列中的元素即可。时间复杂度0(1)

epoll的使用场景

对于多连接,且多连接中只有一部分连接比较活跃时,比较适合使用epoll
例如:典型的一个需要处理上万个客户端的服务器
例如:各互联网APP的入口服务器

epoll的工作方式

水平触发(LT)边缘触发(ET)

1、epoll默认状态下是LT工作模式

例如:
当epoll检测到socket上事件就绪的时候,可以不立刻处理,或只处理一部分
直到所有的数据处理完epoll_wait才不会立刻返回
支持阻塞读写和非阻塞读写

特点:一直通知,分多次读,支持阻塞/非阻塞读写

2、边缘触发ET工作模式

例如:
当epoll检测到socket上事件就绪的时候,必须立刻处理
ET模式下事件只有一次处理的机会
ET的性能比LT更高(epoll_wait,返回的次数更少)Nginx默认采用ET模式的epoll
只支持非阻塞的读写

特点:变更才通知,一次读完,非阻塞读写

ET模式为什么是非阻塞读写?
ET模式只处理一次,即一直读完或出错为止,如果读完了缓冲区数据时对端并没有关闭写端,read函数会一直阻塞,影响其他fd以及后序逻辑。
所以此时fd设置为非阻塞,当没有数据时read读不到数据但是不会被阻塞,可以继续处理后序逻辑。

猜你喜欢

转载自blog.csdn.net/yu876876/article/details/82012806