IO多路复用之select、poll、epoll
关联文章:https://www.cnblogs.com/sfzlstudy/p/15921353.html
why(背景)
unix中的5种常见IO模型:
[2] nonblocking IO - 非阻塞IO
[3] IO multiplexing - IO多路复用
[4] signal driven IO - 信号驱动IO
[5] asynchronous IO - 异步IO
select:
原理:依赖内核进行fd(文件描述符)集合的遍历,找到有可读、写的fd返回。
接口:int select(int maxfdp1,fd_set *readset,fd_set *writeset,fd_set *exceptset,const struct timeval *timeout);
接口说明:maxfdp1指定待测试的文件描述字个数;fd_set
可以理解为一个集合;readset、writeset、exceptset指定要让内核测试读、写和异常条件的文件描述符集合,如果哪个不需要,就设为null;timeout内核等待一个fd就绪的最大时间。返回值,就绪的fd数量,0表示超时,-1表示出错。
缺陷:
1、每次select调用,都涉及fd集合的拷贝(用户态到内核态)。如果fd集合很大就会,有很大开销;
2、每次select调用,都需要遍历fd集合,从而找到可用fd集合。如果fd集合很大就会,有很大开销;
3、每次select调用,fd集合大小有限制(内核为了减少数据拷贝的性能损坏)。其中,这个是通过宏控制的,大小不可改变(限制为1024)
poll:
原理:和select类似,依赖内核进行fd结构链表的遍历,找到有可读、写的fd返回。
接口:int poll(struct pollfd *fds, nfds_t nfds, int timeout);
typedef
struct pollfd { int fd; // 需要被检测或选择的文件描述符 short events; // 对文件描述符fd上感兴趣的事件 short revents; // 文件描述符fd上当前实际发生的事件 } pollfd_t;
接口说明:pollfd结构体的events
域是监视该文件描述符的事件掩码,由用户来设置这个域。结构体的revents
域是文件描述符的操作结果事件掩码,内核在调用返回时设置这个域; nfds为fds
中描述符的总数量。
缺陷:相对select优化了传人的数组结构,改为链表。没有了最多fd数量的限制了。
epoll:
原理:内核使用一个文件描述符,采用红黑色树来管理fd。fd只需要传递一次。fd就绪后,由系统的回调函数,来将fa挂在ready链表上(就绪fd链表)。
接口:
int epoll_create(int size);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
接口说明:
1、epoll_create 函数创建一个epoll句柄,参数size
表明内核要监听的描述符数量。调用成功时返回一个epoll句柄描述符,失败时返回-1。
2、epoll_ctl 函数注册要监听的事件类型。四个参数解释如下:
epfd 表示epoll句柄;
op 表示fd操作类型,有如下3种:
EPOLL_CTL_ADD 注册新的fd到epfd中
EPOLL_CTL_MOD 修改已注册的fd的监听事件
EPOLL_CTL_DEL 从epfd中删除一个fd
fd 是要监听的描述符;
event 表示要监听的事件;
3、epoll_wait 函数等待事件的就绪,成功时返回就绪的事件数目,调用失败时返回 -1,等待超时返回 0:
struct epoll_event { __uint32_t events; /* Epoll events */ epoll_data_t data; /* User data variable */ }; typedef union epoll_data { void *ptr; int fd; __uint32_t u32; __uint64_t u64; } epoll_data_t;
epfd
是epoll句柄;events
表示从内核得到的就绪事件集合;maxevents
告诉内核events的大小;timeout
表示等待的超时事件