什么是I/O复用?I/O复用使得一个程序可以同时监听多个文件描述符。
常见的使用场合:TCP服务器要同时处理监听socket和链接socket。
Linux下实现I/O复用的系统调用只要有select、poll和epoll,epoll是Linux系统独有的。
Select
在一段指定时间内,监听用户感兴趣的文件描述符上的可读、可写和异常等事件。
函数原型:
#include<sys/select.h>
int select(int nfds,struct fd_set *readfds, struct fd_set *writefds, struct fd_set *execpfds,struct timeval* timeout)
- nfds参数指定被监听的文件描述符的总数。它通常被设置为select监听的所有文件描述符中的最大值加1,因为文件描述符是从0开始计数的。
- readfds、writefds和exceptfds参数分别指向可读、可写和异常等事件对应的文件描述符集合。
- timeout参数用来设置select函数的超时时间。
select成功时返回就绪(可读、可写和异常)文件描述符的总数。如果在超时时间内没有任何文件描述符就绪,select将返回0。select失败时返回-1并设置errno。如果在select等待期间,程序接收到信号,则select立即返回-1,并设置errno为EINTR。
Poll
Poll的系统调用和select类似,在指定时间内轮询一定数量的文件描述符,以检测其中是否有就绪者。
函数原型:
#include<poll.h>
int poll(struct pollfd*fds,int nfds,int timeout);
struct pollfd
{
int fd; //用户关注的文件描述符
short events; // 用户关注的事件类型
short revents; //由内核填充
};
epoll
epoll 是Linux特有的I/O函数,epoll使用一组函数来完成任务,epoll把用户关注的文件描述符放在内核的事件表中.
epoll函数的创建:
#include<sys/epoll.h>
int epoll_create(int size)
- size给内核一个提示需要多大的事件表,返回指定的文件描述符表。
对内核事件表进行操作:
int epoll_ctrl(int epdf,int op,int fd,struct epoll_event* event)
- epdf 文件描述符表
- op 指定的操作类型(往事件表中注册、修改、删除fd的注册事件)
- fd 要操作的文件描述符
- event 指定事件,结构体如下:
struct epoll_event
{
__unit32_t events; //epoll事件
Epoll_data_t data; //用户数据
};
epoll系统调用接口: epoll_wait
函数原型:
Int epoll_wait(int epdf,struct epoll_event* events,int maxevents,int timeout)
- maxevents:指定最多监听多少个事件,必须大于0。
成功返回就绪的文件描述符个数,失败返回-1并设置errno
epoll_wait函数如果检测到事件,就将所有就绪的事件从内核事件表(由epfd参数指定)中复制到它的第二个参数events指向的数组中。这个数组只用于输出epoll_wait检测到的就绪事件,所以搜索就绪文件描述符的时间复杂度为O(1),而不像select和poll的数组参数那样既用于传入用户注册的事件,又用于输出内核检测到的就绪事件。这就极大地提高了应用程序索引就绪文件描述符的效率。