IO多路复用
1、select函数
该函数准许进程指示内核等待多个事件中的任何一个发送,并只在有一个或多个事件发生或经历一段指定的时间后才唤醒。函数原型如下:
#include <sys/select.h>
#include <sys/time.h>
int select(int maxfdp1,fd_set *readset,fd_set *writeset,fd_set *exceptset,const struct timeval *timeout);
返回值:就绪描述符的数目,超时返回0,出错返回-1
select在每一轮都会从readset里面返回所有可读的文件描述符,所以根据fd是否设置,对相应的连接进行读写。读写完成后需要重新设置fd标志位。操作系统在接收到客户端发送过来的所有连接在这里都会进行返回。
2、poll函数
# include <poll.h>
int poll ( struct pollfd * fds, unsigned int nfds, int timeout);
struct pollfd {
int fd; /* 文件描述符 */
short events; /* 等待的事件 */
short revents; /* 实际发生了的事件 */
} ;
POLLIN 有数据可读。
POLLRDNORM 有普通数据可读。
POLLRDBAND 有优先数据可读。
POLLPRI 有紧迫数据可读。
POLLOUT 写数据不会导致阻塞。
POLLWRNORM 写普通数据不会导致阻塞。
POLLWRBAND 写优先数据不会导致阻塞。
POLLMSGSIGPOLL 消息可用。
此外,revents域中还可能返回下列事件:
POLLER 指定的文件描述符发生错误。
POLLHUP 指定的文件描述符挂起事件。
POLLNVAL 指定的文件描述符非法。
返回值和错误代码
成功时,poll()返回结构体中revents域不为0的文件描述符个数;如果在超时前没有任何事件发生,poll()返回0;失败时,poll()返回-1,并设置errno为下列值之一:
EBADF 一个或多个结构体中指定的文件描述符无效。
EFAULTfds 指针指向的地址超出进程的地址空间。
EINTR 请求的事件之前产生一个信号,调用可以重新发起。
EINVALnfds 参数超出PLIMIT_NOFILE值。
ENOMEM 可用内存不足,无法完成请求。
3、epoll函数,使用一个fd管理多个fd,将用户关系的fd事件存放到内核的一个事件表中,这样user space和kernel之间复制只需一次。
#include <sys/epoll.h>
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);
epoll_wait分为两种模式,lt:事件可以等下次调用wait时响应;et:事件必须立即处理。et模式减少了epoll事件被重复触发的次数,因此效率要比lt模式高。et模式必须使用非阻塞套接口,避免由于一个文件句柄阻塞读写导致多个任务饿死。
同步和异步io
这里的io操作是指真正的在进行数据的接收发送过程中,进程是否会阻塞。同步io会被阻塞,所以io多路复用会被阻塞。