菜鸟笔记
提升您的技术认知

I/O多路复用之 select

初识 select

  • select系统调用是用来让我们的程序监视多个文件描述符状态变化的;
  • 程序会停在select这里等待,知道被监视的文件描述符有一个或多个发生了状态的改变。
  • 用一句话来总结:select的作用就是监视多个文件描述符状态的变化

select 函数原型

select的函数原型如下: #include

int select(int nfds, fd_set *readfds, fd_set *writefds, 
fd_set *exceptfds, struct timeval *timeout);
参数解释:
  • 参数nfds是需要监视的最大文件描述符值+1,即轮询次数。
  • rdset,wrset,exset分别对应于需要检测的可读文件描述符的集合,可写文件描述符的集合及异常文件描述符的集合;
  • 参数timeout结构为timeval,用来设置select()的等待时间
参数timeout取值:
  • NULL:表示select()没有timeout,select将一直被阻塞,知道某个文件描述符上发生了事件;
  • 0:仅检测描述符集合的状态,然后立即返回,并不等待外部事件的发生。
  • 特定的时间值:如果在指定时间段里没有事件发生,select将超时返回。

关于fd_set结构:

  • 其实这个结构就是一个整数数组,更严格的说,是一个“位图”,使用位图中对应的位来表示要监视的文件描述符。
  • 根据位图的二进制判断哪个文件描述符读事件已经就绪。(内核告诉用户)
  • slelect搭配数组使用,将文件描述符放到数组里。

关于timeval结构

timeval结构用于描述一段时间的长度,如果在这个时间内,需要监视的描述符没有事件发生则函数返回,返回值为0

函数返回值:

  • 执行成功则返回文件描述符词状态已改变的个数
  • 如果返回0代表在描述词状态改变前已超过timeout时间,没有返回

select的特点

总结下来就是:高效,因为是多路转接,效率高

select的缺点

  • 每次调用select,都要手动设置fd集合,从接口使用角度来说也非常不便。
  • 每次调用select,都要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大
  • 同时每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大
  • select关心的文件描述符是有上限的。(底层是位图,有固定大小,鱼竿有上限)
  • 当用户特别多时,效率不一定特别快,可能是平缓