程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> I/O復用-select模型,o復用-select模型

I/O復用-select模型,o復用-select模型

編輯:C++入門知識

I/O復用-select模型,o復用-select模型


IO復用:

I/O復用使得程序可以同時監聽多個文件描述符,這對提高程序的性能至關重要。例如TCP服務器要同時處理監聽socket和連接socket,客戶端要同時處理用戶輸入和網絡連接.

Linux下實現I/O復用的系統調用主要有select、poll和epoll.

select函數:

#include <sys/select.h>
 int select(int nfds,fd_set* readfds,fd_set* writefds,fd_set* excepefds,struct timeval* timeout);

 函數參數介紹:

  (1)nfds:指定被監聽的文件描述符的總數.它通常被設置為select監聽的所有的文件描述中的最大值加1.因為文件描述符是從0開始的.

  (2)readfds、writefd和exceptfds參數:指向可讀、可寫和異常等事件對應的文件描述符集合.select調用返回時,內核將修改它們來通知應用程序哪些文件描述符已經就緒.(fd_set結構體僅包含一個整型數組,該數組的每個元素的每一位(bit)都標記一個文件描述符.fd_set能夠容納的文件描述符數量是由FD_SETSIZE來指定,這就限制了select能同時處理的文件描述符的總量).

  因為位操作比較繁瑣,所以使用下列宏來實現:

   FD_ZERO(fd_set *fdset); //清除fdset的所有位

   FD_SET(int fd,fd_set *fdset);  //設置fdset的位

   FD_CLR(int fd,fd_set *fd_set);  //清除fdset的位fd

   int FD_ISSET(int fd,fd_set *fd_set);//判斷fdset的位fd是否被設置

  (3)timeout參數:被用來設置select函數的超時時間.使用指針參數是因為內核將修改它以告訴用戶select等了多久.

   struct timeval結構體定義:

struct timeval{
    long tv_sec;/*秒數*/
    long tv_usec; /*微秒*/
};
  • timeout變量的tv_sec和tv_usec成員都被設為0,則select將立即返回.
  • timeout=NULL:select將一直阻塞,直到某個文件描述符就緒.

返回值:>0  成功時返回就緒(可讀、可寫和異常)文件描述符的總數.

       =0   在超時時間內沒有任何文件描述符就緒.

       =-1    失敗時,同時並設置errno.

 

例子:利用select接受普通數據和帶外數據都將使select返回.但socket處於不同的就緒狀態:前者處於可讀狀態,後者處於異常狀態.

#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>

int main(int argc,const char* argv[]){
      if(argc<=2){
          printf("usage:%s ip port\n",argv[0]);
          return -1;
      }
      
      const char* ip=argv[1];
      int port=atoi(argv[2]);
      
      struct sockaddr_in address;
      address.sin_family=AF_INET;
      inet_pton(AF_INET,ip,&address.sin_addr);
      address.sin_port=htons(port);
      
      int sockfd=socket(AF_INET,SOCK_STREAM,0);
      assert(sockfd!=-1);
      
      int ret=bind(sockfd,(struct sockaddr*)&address,sizeof(address));
      assert(ret!=-1);
      
      ret=listen(sockfd,5);
      assert(ret!=-1);
      
      struct sockaddr_in client_address;
      socklen_t len=sizeof(client_address);
      int connfd=accept(listenfd,(struct sockaddr*)&client_address,&len);
      assert(connfd>=0);
      
      fd_set readSet;
      fd_set exceptionSet;
      FD_ZERO(&readSet);
      FD_ZERO(&exceptionSet);
      char buf[1024];
      
      while(1){
           memset(buf,'\0',1024);
           
           FD_SET(connfd,&readSet);
           FD_SET(connfd,&exceptionSet);
           
           ret=select(connfd+1,&readSet,NULL,&exceptionSet,NULL);
           if(ret<0){
                  printf("select error\n");
                  break;
           }
          
          if(FD_ISSET(connfd,&readSet)){
              ret=recv(connfd,buf,sizeof(buf),0);
              if(ret<=0){
                  break;
              }
              
              printf("recv data:%s and length:%d\n",buf,ret);
          }
          
          else if(FD_ISSET(connfd,&exceptionSet)){
              ret=recv(connfd,buf,sizeof(buf),MSG_OOB);
              if(ret<=0){
                  break;
              }
              
              printf("recv oob data:%s and length:%d\n",buf,ret);
          }
      }
      
      close(connfd);
      close(sockfd);
      return 0;
}

 


什是i/o復用

當你編寫的程序需要同時處理多個描數字(socket或file或device),你又不知道什麼時候應該(比方說有數據可以讀了)去操作(讀/寫)哪個描數字。這時候I/O復用就需要登場了。

I/O復用是一種讓進程預先“警告”內核能力,使得內核一旦發現進程預先告知時指定的一個或多個I/O條件(就是描述符)就緒(可以讀/寫了),內核就通知進程。linux有4個調用可實現I/O復用:select、poll繼承自Unix系統。pselect是select到Posix版。epoll是linux2.6內核特有的。
 

在UNIX/linux中有4中IO模型還是5種IO模型?

有5種模型.
常用異步IO的路過一下. SIGIO是需要用到信號量的, 資源太受限制. 而常說的這個異步IO這個是操作系統底層通過fd上可都可寫的事件來進行邊緣觸發或者電平觸發, 直接進入回調函數的高效處理方法, 比如說epoll或者kqueue, 不過這個算是相對比較新的技術, 比如說epoll是linux2.6+才有的技術, 在那之前一般用的是多路復用.
 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved