select()函数

北城余情 提交于 2019-12-01 04:53:07

原型:

#include<sys/time.h>
#include<unistd.h>
int select(int maxfd, fd_set *rdset, fd_set *wrest, fd_set *exset, struct timeval *timeout);

参数:

  • maxfd:描述需要监视最大文件描述符+1
  • rdset:监视的可读文件描述符的集合
  • wrset:监视的可写文件描述符的集合
  • exset:监视的异常文件描述符的集合
  • struct timeval:描述一段时间长度,如果在这个时间内,需要监视的描述符没有事件发生,返回0

返回值:

  • 超时返回0
  • 失败返回-1
  • 成功返回大于0的整数,这个整数表示就绪描述符的数目
int FD_ZERO(fd_set *fdset):将指定的文件描述符集情况,在对文件描述符集合进行设置之前,必须对其进行初始化。如果不请空,由于在系统分配内存后,通常不做清空处理,所以结果时不可知的。
int FD_SET(int fd, fd_set *fdset);    用于在文件描述符集合中添加一个新的文件描述符
int FD_CLR(int fd, fd_set *fdset); 用于在文件描述符集合中删除一个新的文件描述符
int FD_ISSET(int fd, fd_set *fdset); 用于测试指定的文件描述符是否在该集合中。

注意:fd_set通常是一个整数数组,其中每个整数中的每一位对应一个描述符(矢量),例如,使用一个32位整数,那么该数组的第一个元素对应于描述符0~31,第二个元素对应于描述符32~63,一次类推。

 

select 系统调用用途在于在一段指定的时间内,监听用户感兴趣的文件描述符上可读可写和异常事件。

 

select使用范例:

当声明了一个文件描述符集之后,必须用FD_ZERo将所有位置置0,然后再将我们所感兴趣的描述符所对应的位置位:

  1. fd_set rset;
  2. int fd;
  3. FD_ZERO(&rset);
  4. FD_SET(fd,&rset);
  5. FD_SET(stdin,&rset);

然后调用select函数,阻塞等待文件描述符事件的到来如果超过设定的事件,则不再等待,继续往下执行

select(fd+1,&rset,NULL,NULL,NULL);

select返回后,用FD_ISSET测试给定位是否置位。

if(FD_ISSET(fd, &rset))
{
…
    //do something
}

深入理解select模型:

理解selecr模型的关键是在于理解fd_set为了说明方便,取fd_set长度为1字节,fd_set的每一位bit可以对应一个文件描述符,则1字节长的fd_set最大可以对应8个fd。

  1. 执行fd_set set; FD_ZERO(&set); 则set用位表示为 0000,0000
  2. 若fd = 5,执行FD_SET(fd,&set);      后set变为0001,0000 (第5位置1)
  3. 若再加入fd = 2,fd = 1,则set变为0001,0011
  4. 执行select(6,&set,0,0,0)阻塞等待
  5. 若fd=1,fd=2上有事件发生,则select返回,此时set变为0000,0001。注意:没有事件发生的fd=5被清空。

 

select模型特点:

  1. 可监控的文件描述符个数取决于sizeof(fd_set)的值。每个bit可以表示一个文件描述符。
  2. 将fd加入到select监控集的同事,还要再使用一个数据结构array保存放到select监控集中的fd,一是用于在select返回后,array作为元数据和fd_set进行FD_ISSET判断。二是select返回后会把以前加入的但无事件发生的fd清空,则每次开始select前都要重新从array取得fd逐一加入人(FD_ZERO最先),扫描array的同时取得fd的最大值maxfd,用于select的第一个参数

可见select模型必须在select前循环加上fd,取maxfd,select 返回后在利用FD_ISSET判断是否有事件发生。

 

select优势:

用户可以在一个线程内同时处理多个socket的IO请求,在网络编程中,当涉及到多客户访问服务器的情况,除了使用fork多个进程来处理每个客户的连接,还可以使用select来处理。

 

select缺点:

select本质是通过设置或者检查存放fd标志位的数据结构来进行下一步处理,这样带来的缺点:

  • l  单个进程可监视的fd数量被限制,即能监听端口的大小有限。一般来说这个数目和系统内存关系很大,具体数目可以cat /proc/sys/fs/file-max查看。
  • l  对socket进行扫描时是线性扫描,即采用轮询的方法,效率较低,当套接字比较多的时候,每次select(0都要通过遍历FD_SETSIZE个socket来完成调度,不管哪个socket是活跃的,都需要遍历一遍。这就很浪费CPU事件,如果能给套接字注册某个回调函数,当他们活跃时,自动完成相关操作,这就避免了轮询,这正是epoll与kqueue做的
  • l  需要维护一个用来存放大量fd的数据结构,这样会使得用户空间和内核空间在传递该结构体时赋值开销大。
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!