APUE之I/O多路复用

I/O多路复用有3种具体实现模型:select、poll和epoll

/* int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout)
 * @nfds        要监听的最大描述符编号加1,select支持的最大描述符编号为FD_SETSIZE(通常是1024)
 * @readfds     要监听可读事件的描述符集合,NULL表示不监听这类事件
 * @writefds    要监听可写事件的描述符集合,NULL表示不监听这类事件
 * @exceptfds   要监听异常事件的描述符集合,NULL表示不监听这类事件
 * @timeout     超时时间,其中NULL表示永远等待,全0表示立即返回(即使没有事件触发)
 * @返回值      如果因为监听的事件被触发则返回关联的描述符总数,同时会修改传入的描述符集合,那些置1的描述符就是被触发了的;
 *              如果因为超时则返回0,同时所有的描述符集合都会被清0;
 *              如果出错则返回-1。
 *
 * 备注:
 *      [1]. select返回-1时需要额外判断errno,因为信号会中断select调用并也返回-1(同时将errno设置为EINTR),这种情况下通常需要重新调用select
 *      [2]. fd_set这个数据类型用于表示描述符集合,其实现跟平台相关,所以对描述符集合只能通过系统提供的4个接口进行操作:
 *                  FD_ZERO(fd_set *fdset)          将一个描述符集合的所有位清0
 *                  FD_SET(int fd,fd_set *fdset)    将一个描述符集合中fd对应的位置1
 *                  FD_CLR(int fd,fd_set *fdset)    将一个描述符集合中fd对应的位清0
 *                  FD_ISSET(int fd,fd_set *fdset)  判断一个描述符集合中fd对应的位是否已置1
 *      [3]. 如果同一描述符的读、写事件都被触发,那么在select返回值中会对其记两次数
 *      [4]. 由于select调用过程中可能会修改传入的描述符集合和timeout,所以再次调用select时需要重新设置这几个入参
 *      [5]. select异常事件主要包括网络连接上有带外数据到达等,需要注意的是异常事件不包括到达文件尾端和TCP连接断开,这两种情况下,select会认为关联的描述符可读,然后调用read系列API,返回0
 *
 * select模型的主要缺陷:
 *          select会修改传入的描述符集合和timeout,导致每次调用select都需要重新初始化这些入参,这对于需要反复执行的场景显然不太合理;
 *	        如果某个描述符监听的条件被触发,select仅仅会返回,但并不告诉用户是哪个描述符,导致用户只能逐个遍历已加入监听的描述符;
 *	        支持的描述符数量太少,缺省是 FD_SETSIZE = 1024
 */


/* int poll(struct pollfd *fds, nfds_t nfds, int timeout)
 * @fds     不同于select使用了3个位图结构fd_set来设置要监听的描述符集合,poll改用pollfd结构体数组实现
 * @nfds    指定了fds数组中元素(即pollfd结构体)的个数
 * @timeout 超时时间,单位ms,其中负值表示永远等待,0表示立即返回(即使没有事件触发)
 * @返回值  如果成功则返回有事件或异常上报的描述符总数;
 *          如果超时则返回0;
 *          如果出错则返回-1
 *
 * 备注:
 *      [1]. poll返回-1时的行为和select类似
 *      [2]. pollfd结构体包含了要监听描述符的相关信息,具体如下
 *                  struct pollfd{
 *                      int fd;         // 一个已经打开了的描述符
 *                      short events;   // 该描述符要监听的事件集合,显然由用户设置
 *                      short revents;  // 该描述符监听的事件集合中触发了的事件集合,或者是POLLERR/POLLHUP/POLLNVAL之一,显然是在poll返回时由内核设置
 *                  };
 *      [3]. poll支持的事件标志主要有:
 *                  POLLIN/POLLRDNORM       有普通数据可读(包括到达文件末尾和TCP连接对端正常关闭等)
 *                  POLLPRI                 有紧急数据可读
 *                  POLLOUT/POLLWRNORM      可不阻塞地写普通数据
 *                  POLLRDHUP               通常流式套接字(比如TCP)对端关闭连接时本端会触发该事件
 *                  POLLERR                 (只会出现在返回的revents中)指定描述符上发生异常,通常会同时附加注册时的POLLIN/POLLOUT事件
 *                  POLLNVAL                (只会出现在返回的revents中)指定描述符没有打开
 *                  POLLHUP                 (只会出现在返回的revents中)指定描述符被挂断,linux平台上的典型例子就是关闭pipe的写端,然后poll pipe的读端,返回的通常就是POLLHUP
 *
 * poll模型相对select模型的主要改进:
 *          poll去掉了描述符数量的限制;
 *          poll不会修改传入的pollfd结构体数组,所以在反复执行poll的场景中不需要再重复进行初始化
 *
 * poll模型仍旧存在的主要缺陷:
 *          poll和select一样都需要在返回后遍历描述符来获取已经触发的事件,那么随着监听的描述符数量增加,必然导致性能明显下降(即便某一时刻只有很少部分处于就绪状态)
 */


/* int epoll_create(int size)       // 创建一个epoll实例
 * @size    早期用于通知内核该新建的epoll实例可以监听的描述符数量,linux 2.6.8后,这个参数被忽略,只是为了保持兼容,所以需要传入一个正数
 * @返回值  成功则返回一个指向新建epoll实例的描述符;出错则返回-1
 *
 * int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)   // 在指定epoll实例中对一个描述符进行操作
 * @epfd    对应一个epoll实例(已经创建)的描述符
 * @op      有效的操作有3种:
 *              EPOLL_CTL_ADD   将一个描述符注册到epoll实例中,同时附带注册了该描述符关联的事件
 *              EPOLL_CTL_MOD   修改一个已经注册的描述符关联的事件
 *              EPOLL_CTL_DEL   注销一个已经注册的描述符
 * @fd      需要进行操作的描述符
 * @event   对应该描述符关联的事件(linux 2.6.9后,传入EPOLL_CTL_DEL时对应的event可以为NULL)
 * @返回值  成功则返回0;出错则返回-1
 *
 * int epoll_wait(int epfd,struct epoll_event *events,int maxevents,int timeout)    // 等待epoll上监听的I/O事件触发
 * @epfd        对应一个epoll实例(已经创建)的描述符
 * @events      返回时用于存放触发事件的数组
 * @maxevents   events数组可以存放的事件数量上限
 * @timeout     超时时间,单位ms,其中-1表示永远等待,0表示立即返回(即使没有事件触发)
 * @返回值      如果成功则返回有事件或异常上报的描述符总数;
 *              如果超时则返回0;
 *              如果出错则返回-1
 *
 * 备注:
 *      [1]. epoll模型属于linux平台特有
 *      [2]. epoll_event结构体包含了要监听描述符关联的相关信息,具体如下
 *                  struct epoll_event{
 *                      uint32_t events;        // 在epoll_ctl中用于设置对应描述符要监听的事件集合(显然由用户设置);在epoll_wait中用于返回对应描述符触发的事件集合(显然是由内核设置)
 *                      epoll_data_t data;      // 在epoll_ctl中用于设置对应描述符的自定义内容(显然由用户设置);在epoll_wait中用于返回对应描述符事先注册的自定义内容(显然是由内核设置)
 *                  };
 *                  typedef union epoll_data{
 *                      void *ptr;
 *                      int fd;
 *                      uint32_t u32;
 *                      uint64_t u64;
 *                  };
 *      [3]. epoll支持的事件标志主要有:
 *                  EPOLLIN         类似POLLIN
 *                  EPOLLPRI        类似POLLPRI
 *                  EPOLLOUT        类似POLLOUT
 *                  EPOLLRDHUP      类似POLLRDHUP
 *                  EPOLLERR        类似POLLERR
 *                  EPOLLHUP        类似POLLHUP
 *                  EPOLLET         设置对应的描述符为边沿触发(缺省是水平触发)
 *                  EPOLLONESHOT    设置对应的描述符只做一次性监听(缺省是持久生效的),意味着触发过一次后就会失效
 *
 * epoll模型相对select模型和poll模型的主要改进:
 *          当某个描述符关联的事件触发时,epoll通过回调机制直接将对应的描述符通知用户,从而去掉了遍历操作,
 *          这种改进使得epoll模型中的I/O效率基本不会随着监听的描述符数量增加而下降
 *                    
 */


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值