Heim  >  Artikel  >  Betrieb und Instandhaltung  >  Warum Select unter Linux verwenden?

Warum Select unter Linux verwenden?

青灯夜游
青灯夜游Original
2023-01-30 19:04:541258Durchsuche

Da Select es Entwicklern ermöglicht, gleichzeitig auf mehrere Dateipuffer zu warten, kann dies die E/A-Wartezeit verkürzen und die E/A-Effizienz des Prozesses verbessern. Die Funktion select() ist eine E/A-Multiplexfunktion, die es dem Programm ermöglicht, mehrere Dateideskriptoren zu überwachen und darauf zu warten, dass einer oder mehrere der überwachten Dateideskriptoren „bereit“ werden. Bezieht sich auf: die Datei Der Deskriptor ist nicht mehr blockiert und kann für bestimmte Arten von E/A-Vorgängen verwendet werden, einschließlich lesbarer, beschreibbarer und Ausnahmen.

Warum Select unter Linux verwenden?

Die Betriebsumgebung dieses Tutorials: Linux7.3-System, Dell G3-Computer.

select ist eine Computerfunktion, die sich in der Header-Datei #include befindet. Diese Funktion wird verwendet, um Dateideskriptoränderungen zu überwachen – Lesen, Schreiben oder Ausnahmen.

1. Einführung in die Auswahlfunktion

Die Hauptfunktion besteht darin, darauf zu warten, dass das Ereignis im Dateideskriptor bereit ist. Mit der Auswahl können wir gleichzeitig auf mehrere Dateipuffer warten Zeitbereich, wodurch die E/A-Wartezeit reduziert und die E/A-Effizienz des Prozesses verbessert wird. Mit der Funktion

select() kann das Programm mehrere Dateideskriptoren überwachen und warten, bis einer oder mehrere der überwachten Dateideskriptoren „bereit“ sind. Der sogenannte „Bereit“-Zustand bedeutet: Der Dateideskriptor befindet sich nicht mehr in einem blockierenden Zustand und kann für bestimmte Arten von E/A-Vorgängen verwendet werden, einschließlich lesbarer, beschreibbarer und Ausnahmevorfälle.

2 Einführung in die Auswahl von Funktionsparametern

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

ndfs

Der Maximalwert des Dateideskriptors, der auf +1 wartet, zum Beispiel: Wenn der Anwendungsprozess auf Ereignisse der Dateideskriptoren 3, 5, 8 warten möchte, dann

nfds=max(3,5,8)+1;

fd_set-Typ

readfds Die Typen von writefds und außerfds sind alle fd_set. Was ist also der Typ fd_set?

  • fd_set-Typ ist im Wesentlichen eine Bitmap , die Position von bitmap repräsentiert den entsprechenden Dateideskriptor, der Inhalt gibt an, ob der Dateideskriptor gültig ist, 1 stellt den Dateideskriptor an dieser Position dar. Gültig, 0 bedeutet, dass der Dateideskriptor an diesem Speicherort ungültig ist.
  • Wenn die Dateideskriptoren 2 und 3 in der Bitmap festgelegt sind, stellt die Bitmap 1100 dar.
  • Die Obergrenze von fd_set beträgt 1024 Dateideskriptoren.

readfds

  • readfds ist eine Sammlung von Dateideskriptoren, die auf Leseereignisse warten. Wenn Sie sich nicht für Leseereignisse interessieren (der Puffer enthält Daten), können Sie einen NULL-Wert übergeben .
  • Sowohl der Anwendungsprozess als auch der Kernel können readfds festlegen, um den Kernel zu benachrichtigen, auf das Leseereignis des Dateideskriptors in readfds zu warten readfds wird gesetzt, um zu sagen, welche Leseereignisse des Bewerbungsprozesses wirksam werdenwritefds
  • Ähnlich wie readfds ist writefds eine Sammlung, die auf Schreibereignisse wartet (
ob Platz vorhanden ist). im Puffer)

Wenn Sie sich nicht für Schreibereignisse interessieren, können Sie den Wert NULL übergeben. Warum Select unter Linux verwenden?

exclusivefds

Wenn der

-Kernel darauf wartet, dass der entsprechende Dateideskriptor eine Ausnahme

erhält, dann setzt

den fehlgeschlagenen Dateideskriptor inexclusedfds

Wenn Ihnen Fehlerereignisse egal sind, können Sie dies tun Übergeben Sie den Wert NULL.

timeout Stellen Sie die Zeitauswahlblöcke im Kernel ein. Wenn Sie es auf nicht blockierend einstellen möchten, setzen Sie es auf NULL. Wenn Sie eine Blockierung für 5 Sekunden auswählen möchten, erstellen Sie einen struct timeval time={5,0}

;

wobei der Strukturtyp von struct timeval ist:
           struct timeval {
               long    tv_sec;         /* seconds */
               long    tv_usec;        /* microseconds */
           };

RückgabewertIf Es gibt keine Datei. Geben Sie 0 zurück, wenn der Deskriptor bereit ist.

Gib -1 zurück, wenn der Aufruf fehlschlägt.

Wenn in readfds ein Ereignis auftritt, wird die verbleibende Zeitüberschreitung zurückgegeben.

    3.selects Workflow
  • Anwendungsprozess
und

Kernel

müssen beide Informationen von readfds und writefds erhalten. Unter anderem muss der Kernel wissen, welche Dateideskriptoren von readfds und warten müssen writefds, Der Anwendungsprozess muss wissen, welche Dateideskriptorereignisse von readfds und writefds bereitstehen.

如果我们要不断轮询等待文件描述符,则应用进程需要不断的重新设置readfds和writefds,因为每一次调用select,内核会修改readfds和writefds,所以我们需要在 应用程序设置一个数组 来保存程序需要等待的文件描述符,保证调用 select 的时候readfds 和 writefds中的将如下:

Warum Select unter Linux verwenden?

4.Select服务器

 如果是一个select服务器进程,则服务器进程会不断的接收有新链接每个链接对应一个文件描述符,如果想要我们的服务器能够同时等待多个链接的数据的到来,我们监听套接字listen_sock读取新链接的时候,我们需要将新链接的文件描述符保存到read_arrys数组中,下次轮询检测的就会将新链接的文件描述符设置进readfds中,如果有链接关闭,则将相对应的文件描述符从read_arrys数组中拿走

一张图看懂select服务器:

Warum Select unter Linux verwenden?

简易版的select服务器:

server.hpp文件:

#pragma once  
  #include<iostream>
  #include<sys/socket.h>
  #include<sys/types.h>    
  #include<netinet/in.h> 
  #include<string.h>
  using std::cout;
  using std::endl;
  #define BACKLOG 5  
      
  namespace sjp{    
    class server{    
      public:    
      static int Socket(){    
        int sock=socket(AF_INET,SOCK_STREAM,0);    
        if(sock>0)    
        return sock;    
        if(sock<0)    
          exit(-1);    
   }    
      
      static bool Bind(int sockfd,short int port){    
        struct sockaddr_in lock;    
        memset(&lock,&#39;\0&#39;,sizeof(lock));    
        lock.sin_family=AF_INET;    
        lock.sin_port=htons(port);    
        lock.sin_addr.s_addr=INADDR_ANY;    
        if(bind(sockfd,(struct sockaddr*)&lock,(socklen_t)sizeof(lock))<0){    
                  exit(-2);
        }    
        return true;    
      }    

     static bool Listen(int sockfd){
        if(listen(sockfd,BACKLOG)<0){
          exit(-3);
        }
        return true;
      }
    };
  }

 select_server.hpp文件

#pragma once
  #include<vector>
  #include"server.hpp"
  #include<unistd.h>
  #include<time.h>
  
  namespace Select{
    class select_server{
      private:
        int listen_sock;//监听套接字    
        int port;    
          
      public:    
        select_server(int _port):port(_port){}    
      
        //初始化select_server服务器    
        void InitServer(){    
          listen_sock=sjp::server::Socket();    
          sjp::server::Bind(listen_sock,port);    
          sjp::server::Listen(listen_sock);    
        }    
      
      
        void Run(){    
          std::vector<int> readfds_arry(1024,-1);//readfds_arry保存读事件的文件描述符    
          readfds_arry[0]=listen_sock;//将监听套接字保存进readfds_arry数组中    
          fd_set readfds;    
          while(1){    
          FD_ZERO(&readfds);    
          int nfds=0;    
          //将read_arry数组中的文件描述符设置进程readfds_arry位图中    
          for(int i=0;i<1024;i++)    
          {    
            if(readfds_arry[i]!=-1){    
            FD_SET(readfds_arry[i],&readfds);    
           if(nfds<readfds_arry[i]){
              nfds=readfds_arry[i];
            }
            }
          }
  
          //调用select对readfds中的文件描述符进行等待数据
          switch(select(nfds+1,&readfds,NULL,NULL,NULL)){
            case 0:
              //没有一个文件描述符的读事件就绪
              cout<<"select timeout"<<endl;
              break;
            case -1:
              //select失败
              cout<<"select error"<<endl;
            default:
              {
              //有读事件发生
                Soluation(readfds_arry,readfds);
                break;
              }
          }           
          }
        }
        void Soluation(std::vector<int>& readfds_arry,fd_set readfds){
        for(int i=0;i<readfds_arry.size();i++){
            if(FD_ISSET(readfds_arry[i],&readfds))
            {
              if(readfds_arry[i]==listen_sock){
                //有新链接到来
                struct sockaddr peer;
                socklen_t len;
                int newfd=accept(listen_sock,&peer,&len);
                cout<<newfd<<endl;
                //将新链接设置进readfds_arry数组中
                AddfdsArry(readfds_arry,newfd);
              }
              else{
                //其他事件就绪
                char str[1024];
                int sz=recv(readfds_arry[i],&str,sizeof(str),MSG_DONTWAIT);
                switch(sz){
                  case -1:
                    //读取失败
                    cout<<readfds_arry[i]<<": recv error"<<endl;
                    break;
                  case 0:
                    //对端关闭
                    readfds_arry[i]=-1;
                    cout<<"peer close"<<endl;
                    break;
                  default:
                    str[sz]=&#39;\0&#39;;
                    cout<<str<<endl;
                    break;
                }
              }
            }
          }
        }

        void AddfdsArry(std::vector<int>& fds_arry,int fd){
       for(int i=0;i<fds_arry.size();i++){
            if(fds_arry[i]==-1){
              fds_arry[i]=fd;
              break;
            }
          }
        }
    };
  }

 select_server.cc文件

#include"select_server.hpp"

int main(int argv,char* argc[]){
  if(argv!=2){
    cout<<"./selectserver port"<<endl;
    exit(-4);
  }                                                                                                 
  int port=atoi(argc[1]);//端口号
  Select::select_server* sl=new Select::select_server(port);
  sl->InitServer();
  sl->Run();
}

测试:

Warum Select unter Linux verwenden?

Warum Select unter Linux verwenden?

5.Select的缺陷

  • 由于fd_set的上限是1024,所以select能等待的读事件的文件描述符和写事件的文件描述是有上限的,如果作为一个大型服务器,能够同时链接的客户端是远远不够的。
  • 每次应用进程调用一次select之前,都需要重新设定writefds和readfds,如果进行轮询调用select,这对影响cpu效率。
  • 内核每一次等待文件描述符 都会重新扫描所有readfds或者writefds中的所有文件描述符,如果有较多的文件描述符,则会影响效率。

推荐学习:Linux视频教程

Das obige ist der detaillierte Inhalt vonWarum Select unter Linux verwenden?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn