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: 会修改输入的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相关》。