0%

select,poll,epoll总结

1. 简介

都是IO多路复用。

可以在一个进程或线程中同时管理多个IO的事件,在相当于在一个点去等待这些事件,收集到事件后,再分别处理。

2. 基本API

2.1. select

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 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: 会修改输入的fdsrevents字段,因此也需要用户检查每个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相关》。