初识 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关心的文件描述符是有上限的。(底层是位图,有固定大小,鱼竿有上限)
- 当用户特别多时,效率不一定特别快,可能是平缓