使用I/O复用的场景:
(1)当客户处理多个文件描述符时(一般是交互式输入和网络套接口),必须是I/O复用
(2)当一个客户端同时处理多个套接口时,而这种情况是可能的,但很少出现
(3)如果一个TCP服务器既要处理监听套接口,又要处理已连接套接口,一般也要用到I/O复用
(4)如果一个服务器既要处理TCP,又要处理UDP,这时要用I/O复用
(5)如果一个服务器处理多个服务或多个协议,这时要用到I/O复用
select
int select(int nfds ,fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
ndfs 参数是最大文件描述符+1
fd_set:记录文件描述符 监听的文件描述符
readfds:可读事件
writefds:可写事件
exceptfds:异常事件
timeout 超时时间 NULL:永久阻塞,直到有文件描述符上有事件发生。
返回值 -1 出错
0 超时
>0 有文件描述符就绪,返回就绪的文件描述符个数
select每次要将文件描述符从用户态拷贝到内核态中,返回值有就绪的文件描述符也有未就绪的文件描述符,故每次查看是否就绪要经过轮训的方式查找时间复杂度为O(n。所以每次调用select之前必须要重新设置readfds,writefds,exceptfds.
poll
int poll(struct pollfd* fds, nfds_t nfds, int timeout)
(1)
struct pollfd
{
int fd; /*文件描述*/
short events; /*注册的事件*/
short revents; /*实际发生的事件*/
}
fd成员指定文件描述符;
events成员告诉poll监听fd上的那些事件,它是一系列事件的按位或;
revents成员由内核修改,以通知应用程序fd上实际发生了那些事件。
(2)
nfds 参数指定被监听事件集合fds的大小,其类型nfds_t的定义如下:
typedef unsigned long nfds_t;
(3)
timeout参数指定poll的超时值,单位是毫秒。当timeout为-1时,poll调用将永远阻塞,直到某个事件发生;当timeout为0时,poll调用将立即返回.
返回值 -1 出错
0 超时
>0 有文件描述符就绪,返回就绪的文件描述符个数
poll的返回值每次都返回就绪的和未就绪的,故每次查看是否就绪要经过轮训的方式查找时间复杂度为O(n)
epoll
epoll使用一组函数来完成任务。epoll把用户关系的文件描述符上的事件放在内核里的一个事件表中,从而无须像select和poll那样每次调用都要重复传入文件描述符集或事件集。但epoll需要使用一个额外的文件描述符,来唯一标识内核中的这个事件表
int epoll_create(int size);
size给内核一个提示,告诉它事件表需要多大。该函数的返回的文件描述符将用于其他epoll系列函数的第一个参数
int epoll_ctl(int epfd, int op, int fd, struct epoll_event* event);
fd参数是要操作的文件描述符,op参数则指定操作类型。操作类型有如下三种:
EPOLL_CTL_ADD,往事件表中注册fd上的事件
EPOLL_CTL_MOD,修改fd上的注册事件
EPOLL_CTL_DEL,删除fd上的注册事件
event参数指定事件,它是epoll_event结构指针类型。epoll_event的定义如下:
struct epoll_event
{
_uint32_t events; /*epoll事件*/
epoll_data_t data; /*用户数据*/
}
typedef union epoll_data
{
void* ptr;
int fd;
uint32_t u32;
uint64_t u64;
}epoll_data_t;
int epoll_wait(int epfd, struct epoll_event* events, int maxevents, int timeout);
epoll_wait函数它在一段超时时间内等待一组文件描述符上的事件
该函数成功时返回就绪的文件描述符的个数,失败时返回-1并设置errno
timeout参数指定epoll的超时值,单位是毫秒。当timeout为-1时,epoll调用将永远阻塞,直到某个事件发生;当timeout为0时,epoll调用将立即返回.
select、poll、epoll区别
select可存放的文件描述符是fd_set中有一个FD_SETSIZE宏是1024,所以限制了存放文件描述符的个数为1024个(0~1023),poll和epoll都是通过一个int类型的参数来存放文件描述符,比select可存放的文件描述符多。
select通过32个long类型的数组按位存放文件描述符的,poll通过用户数组记录所有的文件描述符,epoll同过内核事件表记录。
select通过三个结构体关注用户的文件描述符,故每次返回的就绪文件描述符和未就绪的文件描述符通过这三个结构体接收,所以每次调用select之前要每次都要重新设置三个结构体。poll将用户关注的事件和内核反馈发生的事件分开表示,epoll通过数组返回就绪的内核事件,epoll和poll不需要每次调用前重新设置
select和poll返回的是就绪的文件描述符和未就绪的文件描述符,每次要用轮训的方式进行查找哪个是就绪的所以查找的事件复杂度为O(n).epoll每次返回的是就绪的文件描述符,查找就绪的文件描述符每次用回调的方法,时间复杂度为O(1)
select内核是数组,poll内核是链表,epoll内核是红黑树+链表
select和poll仅仅支持LT模式,epoll支持高效的ET模式
select和poll都是单独的函数,epoll是一组函数
select和epoll调用时要将用户态的文件描述符拷贝到内核态中,修改之后又要讲内核态的文件描述符拷贝到用户态中。epoll只用将修改后的文件描述符拷贝到用户态中。