Linux IO多路复用模型 : select epoll

前言

为什么要使用IO多路转接模型?
试想一下,如果A想接收B的消息,那么A就需要创建一个进程(或线程),用while(1)循环一直监视B是否给A发来消息,同时B也一直需要监视A,这会让系统的开销变得很大。
IO多路转接模型的函数select, 可以帮助我们同时监视多个文件句柄, 这样极大地降低了 内存 与 CPU 的开销。

select

  • int select(int maxFd, fdset* rdset, fdset* wrset, fdset* exceptionset, struct timeval*)
    select函数的本质:将加入的文件句柄进行监视, 如果读集合有可读的情况 或者 写集合有可写的情况,那么将向下执行
    出错时返回 -1
    超时返回 0
    正常情况下返回一个 大于0 的数

参数说明
maxfd: 文件句柄的最大值+1,如0, 1, 2分别代表输入,输出,错误输出,那么maxfd应该是现有的fd的最大值+1, select的本质是循环

rdset: 读文件句柄的集合,当检测的文件句柄中有可读的情况时,开始往下执行

wrset: 写文件句柄的集合,当检测的文件句柄中有可写的情况时,开始往下执行,一般来说文件句柄均为可写的情况

exceptionset: 异常文件句柄的集合

struct timeval:等待时间 NULL表示select函数阻塞并无限等待,0表示select函数非阻塞不等待,传入timeval则表示阻塞等待的时间

文件句柄的函数:

  • FD_ZERO(fd_set* sets)
    清空sets集合中原来的文件句柄
  • FD_SET(int fd, fd_set* sets)
    添加文件句柄到集合中
  • FD_ISSET(int fd, fd_set* sets)
    判断文件句柄在sets集合中是否有设定

epoll

epoll相比于select有三个优点

  1. select在每一次循环的时候都需要重新设置需要监听的句柄,epoll则不必
  2. select对每一个需要监听的句柄都要遍历询问,epoll则是直接将准备就绪的句柄加入就绪队列,并返回就绪句柄数量
  3. epoll的最大连接数可以设置得非常的大,而select则只能设置很少

select相当于O(N)的时间复杂度,当有句柄准备就绪时,它并不知道是哪一个准备就绪,只能进行轮询;
epoll相当于O(1)的时间复杂度,它会精准的知道是哪些句柄准备就绪,并返回就绪句柄的数量。
在有大量的并发连接,且真正需要数据交换的有效连接较少的情况下,epoll的效率则比select大了许多。

  • int epoll_create(int __size)

size目前这个参数已经不被需要,我们通常将它设置为1.
成功则返回一个epoll句柄,失败返回-1

  • int epoll_ctl(int __epfd, int __op, int __fd, epoll_event * event)

epfd:epoll句柄
op: 需要进行的操作,有 EPOLL_CTL_ADD, EPOLL_CTL_MOD, EPOLL_CTL_DEL
fd : 需要进行操作的句柄
event : 一个结构体, 用于与op结合进行操作

  • int epoll_wait(int __epfd, epoll_event *__events, int __maxevents, int __timeout)

epfd: epoll句柄
events: 传入event数组,将准备就绪的句柄赋值到event数组中
maxevents: event数组的容量
timeout: 超时机制,-1则是永久等待,0则是不等待,正数则是等待毫秒
返回值:成功则返回的是准备就绪的句柄数量,也就是event数组被赋值的数量,失败返回-1, 超时返回0
epoll_wait在函数的形式上接近select


实战项目

一个基于epoll 实现与 select 实现的聊天室项目:

Linux实战项目 聊天室

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章