select 系统调用的定义如下:
select(int nfds, fd_set* readfds, fd_set* writefds, fd_set* exceptfds, struct time_val* timeout);
select 系统调用时,向操作系统内核空间会拷贝 3 个 fd_set,分别表示要监控读事件,写事件以及异常事件的 fd 集合,之后内核会对这些 fd 进行轮询,并将相应事件就绪的 fd 拷贝至对应的 fd_set 并在调用结束后将写好的 fd_set 返回至用户空间。
fd_set 内部采用 bitmask 方式进行实现,因此打开的最大 fd 的数目受到一定的限制,由 FD_SETSIZE 常量表示,可以采用如下的 API 进行设置。
#include <sys/select.h>
void FD_ZERO(fdset* fds); /* 将fdset置空 */
void FD_SET(int fd, fdset* fds); /* fdset添加fd */
void FD_CLR(int fd, fdset* fds); /* fdset删除fd */
int FD_ISSET(int fd, fdset* fds); /* 当前fd是否设置,若设置返回1,否则返回0*/
poll 系统调用的定义如下:
poll(struct pollfd fds[] , int nfds, int timeval);
poll 系统调用的基本流程与 select 类似,区别是传入的是 pollfd 的数组而非 fd_set(bitmask)。因此其数量不会受到限制,在每个 pollfd 结构体中,会标明当前监视的 fd 事件的 bitmask 也就是 events,每次 poll 调用结束,内核会修改 revents 并将其返回。
由于 poll 传入的是一个数组而并非二进制掩码,所以其大小没有限制,使用时只需设置 struct pollfd 相应字段即可。
struct pollfd 的结构如下所示:
struct pollfd{
int fd; /* 监听的文件描述符。 */
short events; /* 监听事件的bitmask,POLLIN,POLLOUT等 */
short revents; /* 返回事件的bitmask */
}
3.epoll
epoll 系统调用主要提供了 3 个 API:
int epoll_create(int size);
int epoll_ctl(int epfd, int op , int fd , struct epoll_event *ev);
int epoll_wait(int epfd, strct epoll_event* ev,int maxevents ,int timeout);
epoll_create 创建了一个 epoll 的实例,该实例的 interest_list 为空,调用返回一个代表该 epoll 实例的文件描述符,这个文件描述符不是用于 IO,它代表了一个内核数据结构的句柄。
size 参数指定了一个想要通过 epoll 实例来检查文件描述符的个数,这个参数并不代表着一个上限,而是告诉内核如何为内部结构划分初始大小。
epoll_ctl 能够修改 epoll 实例对应的 interest_list,包括向兴趣列表中添加,修改和删除 fd 的数据等。
op 操作包括:EPOLL_CTL_ADD,EPOLL_CTL_MOD 和 EPOLL_CTL_DEL。
epoll_event 的结构如下所示:
struct epoll_event {
uint32_t events; /* events is the bitmask of the epoll_event*/
epoll_data_t data; /* data is a union struct of the user_data */
}
typedef union epoll_data{
void *ptr;
int fd;
uint32_t u32;
uint64_t u64;
}
epoll_wait 返回 epoll 实例中就绪的文件描述符信息,返回值为当前就绪的 fd 数量,返回的 event 信息存储在 evlist 数组中,evlist 的空间由用户自行申请,并指定最大返回的事件数量 maxevents 。
timeout 字段用来确定 epoll_wait 的阻塞行为。
返回事件的种类存储在 evlist 的各个 events 中,使用 bitmask 表示。
常见 events 字段的 bitmask 值有以下几种: