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

原文地址:https://www.cnblogs.com/suntus/p/14909760.html