select,poll,epoll总结
1. 简介
都是IO多路复用。
可以在一个进程或线程中同时管理多个IO的事件,在相当于在一个点去等待这些事件,收集到事件后,再分别处理。
2. 基本API
2.1. select
// 1. 操作fd集
void FD_CLR(int fd, fd_set *set);
int FD_ISSET(int fd, fd_set *set);
void FD_SET(int fd, fd_set *set);
void FD_ZERO(fd_set *set);
// 2. 等待事件
struct timeval {
time_t tv_sec; /* seconds */
long tv_usec; /* microseconds */
};
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
struct timespec {
long tv_sec; /* seconds */
long tv_nsec; /* nanoseconds */
};
int psel
int pselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timespec *timeout, const sigset_t *sigmask);
2.2. poll
// 1. 操作fd集
struct pollfd {
int fd; /* file descriptor */
short events; /* requested events */
short revents; /* returned events */
};
// 2. 等待事件
// timeout 单位是毫秒(ms)
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
struct timespec {
long tv_sec; /* seconds */
long tv_nsec; /* nanoseconds */
};
int ppoll(struct pollfd *fds, nfds_t nfds, const struct timespec *timeout_ts, const sigset_t *sigmask);
2.3. epoll
// 1.创建
int epoll_create(int size); // size被忽略,但必须大于0
int epoll_create1(int flags);
// 2. 操作fd集
typedef union epoll_data {
void *ptr;
int fd;
uint32_t u32;
uint64_t u64;
} epoll_data_t;
struct epoll_event {
uint32_t events; /* Epoll events */
epoll_data_t data; /* User data variable */
};
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
// 3. 等待事件
// timeout单位是毫秒(ms)
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
int epoll_pwait(int epfd, struct epoll_event *events, int maxevents, int timeout, const sigset_t *sigmask);
3. 对比
3.1. 监听fd集
- select:使用
fd_set
的bitmask来表示监听fd集合,并有一组宏来操作这个bitmask;不同事件有不同的fd集合; - poll:使用
struct pollfd
的数组来表示监听fd集合,结构体保存有该fd上需要监听的事件,集合的增加减少就直接由用户在数组中添加减少项来完成; - epoll: 使用
struct epoll_event
来表示一个fd对应的监听事件,有一组接口控制fd和其上的监听事件。
3.2. 如何返回产生事件的fd
- select: 会修改输入的
fd_set
,用户需要依次检查每个fd是否在返回的监听fd集合中,效率低; - poll: 会修改输入的
fds
的revents
字段,因此也需要用户检查每个fd是否有事件产生,效率低; - epoll: 会直接返回有事件发生的
events
数组,用户不需要检查,因为返回的数组中都是有事件的fd,效率高。
3.3. 阻塞时间
- select:
select(2)
使用struct timeval
结构体,精确到微秒(us),这个是UNIX使用的时间。pselect(2)
使用struct timespec
结构体,精确到纳秒(ns),这个是POSIX规定的结构体。 - poll:
poll(2)
使用int,单位是毫秒(ms),ppoll(2)
使用struct timespec
,精确到纳秒(ns); - epoll:
epoll_wait(2)
和epoll_pwait(2)
都使用int,精确到毫秒(ms);
3.4. 带不带p
可以看到,这三个都有两个版本,select/pselect, poll/ppoll, epoll_wait/epoll_pwait
,主要区别都是对信号的处理。详见《select相关》。