IO多路复用之select函数详解

select优点

1)select()的可移植性更好,在某些Unix系统上不支持poll()
2)select() 对于超时值提供了更好的精度:微秒,而poll是毫秒。

select缺点
1) 单个进程可监视的fd数量被限制。
2) 每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大
同时每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大
3) 对fd进行扫描时是线性扫描。fd剧增后,IO效率较低,因为每次调用都对fd进行线性扫描遍历,所以随着fd的增加会造成遍历速度慢的性能问题
4)select() 函数的超时参数在返回时也是未定义的,考虑到可移植性,每次在超时之后在下一次进入到select之前都需要重新设置超时参数。

api原型:

#include <sys/time.h>
#include <sys/select.h>

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout)

    • 每个参数不介绍了,这里有几点说明
      nfds这个参数必须是三个文件描述符集合中所包含的最大的文件描述符号+1。该参数让select()变得更有效率, 因为此时内核就不用去检查大于这个值得文件描述符号是否属于这些文件描述符集合。

    • 一些宏

    #include <sys/select.h>
    void FD_ZERO(fd_set *fdset);
    void FD_SET(int fd, fd_set *fdset);
    void FD_CLR(int fd, fd_set *fdset);
    int FD_ISSET(int fd, fd_set *fdset);
    //具体实现如下
    #define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= (1L << ((n) % NFDBITS)))
    #define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= ~(1L << ((n) % NFDBITS)))
    #define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & (1L << ((n) % NFDBITS)))
    #define FD_ZERO(p) (__extension__ (void)({ \
         size_t __i; \
         char *__tmp = (char *)p; \
         for (__i = 0; __i < sizeof (*(p)); ++__i) \
           *__tmp++ = 0; \
    }))
     
     

      上述宏安如下方式工作
      1、FD_ZERO将fdset所指的集合初始化为空
      2、FD_SET将fd添加到fdset中
      3、FD_CLR将fd从fdset中删除
      4、FD_ISSET表示如果fd在fdset中则返回1,否则为0

      当timeout为NULL, 或者指向的结构体字段为非0时, select()将阻塞直到有下列事件发生:
      1、三个文件描述符集合至少有一个成为就绪态
      2、该调用被信号处理例程打断
      3、timeout中指定的时间上限已经超时了

      常用的框架代码:

      int fds[128];
      const int len = 128;
      
      
      int startup(char *ip,int port)
      {
          int listen_sock = socket(AF_INET,SOCK_STREAM,0);
          struct sockaddr_in local;
          local.sin_family = AF_INET;
          local.sin_addr.s_addr = inet_addr(ip);
          local.sin_port = port;
          bind(&listen_sock,(struct sockaddr*)&local,sizeof(local));
          if(listen(listen_sock,5) < 0)
          {
              perror("listen");
          }
          return listen_sock;
      }
      
      int main()
      {
          if(argc != 3)
          {
              usage(argv[0]);
              exit(1);
          }
          int i = 0;
          for(;i < len;i++)
          {
              fds[i] = -1;
          }
      
          int listen_sock = startup(argv[1],atoi(argv[2]));
      
          fd_set rfds;
          fds[0] = listen_sock;
          int done;
          while(!done)
          {
              int max_fd = -1;
              FD_ZERO(&rfds);
              for(int i = 0;i < len;i++)
              {
                  if(fds[i] != -1)
                  {
                      FD_SET(fds[i],&rfds);
                  }
                  max_fd = fd[i] > max_fd ? fd[i]:max_fd;
              }
              struct timeval timeout = {5,0};
              switch(select(max_fd+1,&rfds,NULL,NULL,NULL))
              {
                  case 0:
                  printf("timeout\n");
                  break;
                  case -1:
                  perror("select");
                  break;
                  default:
                  {
                      if(FD_ISSET(listen_sock,&rfds))
                      {
                          struct sockaddr_in peer;
                          int len = sizeof(peer);
                          int new_fd = accept(listen_sock,\
                          (struct sockaddr*)&peer,&len);
                          if(new_fd > 0)
                          {
                              printf("get a new client->%s:%d\n",\
                              inet_addr(peer.sin_addr),\
                              ntohs(peer.sin_port));
                              for(int i = 0; i < len;i++)
                              {
                                  if(fds[i] == -1)
                                  {
                                      fds[i] = new_fd;
                                      break;
                                  }
                              }
                              if(i == len)
                              {
                                  close(new_fd);
                              }
                          }
                      }
                      else
                      {
                          char buf[1024];
                          for(int i = 0; i < len;i++)
                          {
                              if(i != 0 && FD_ISSET(fds[i],\
                                              &rfds))
                              {
                                  ssize_t _s = read(fds[i],buf,sizeof(buf))
                                  if(_s > 0)
                                  {
                                      buf[_s] = '\0';
                                      printf("client:%s\n",buf);
                                  }
                                  else if(_s == 0)
                                  {
                                      printf("client:%d is close\n",fds[i]);
                                      close(fds[i]);
                                      fds[i] = -1;
                                  }
                                  else 
                                      perror("read");
                              }
                          }
                      }
                  }
                  break;          
              }
          }
      }
      
      • 0
        点赞
      • 0
        收藏
        觉得还不错? 一键收藏
      • 0
        评论
      评论
      添加红包

      请填写红包祝福语或标题

      红包个数最小为10个

      红包金额最低5元

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

      抵扣说明:

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

      余额充值