Maison  >  Article  >  Opération et maintenance  >  Quelle est la raison pour laquelle Linux utilise select ?

Quelle est la raison pour laquelle Linux utilise select ?

WBOY
WBOYavant
2023-05-19 15:07:441002parcourir

L'utilisation de la fonction select permet aux développeurs d'attendre plusieurs tampons de fichiers en même temps, réduisant ainsi le temps d'attente des IO et améliorant l'efficacité des IO du processus. La fonction select() est une fonction de multiplexage IO qui permet au programme de surveiller plusieurs descripteurs de fichiers et d'attendre qu'un ou plusieurs des descripteurs de fichiers surveillés deviennent « prêts » ; l'état dit « prêt » fait référence à : le fichier ; Le descripteur n'est plus bloqué et peut être utilisé pour certains types d'opérations d'E/S, y compris les opérations en lecture, en écriture et les exceptions.

#includeLe fichier d'en-tête contient la fonction de sélection, qui peut être appelée sur l'ordinateur. Cette fonction est utilisée pour surveiller les modifications du descripteur de fichier : lecture, écriture ou exceptions.

1. Introduction à la fonction select

La fonction select est une fonction de multiplexage IO. Sa fonction principale est d'attendre que l'événement dans le descripteur de fichier soit prêt. La sélection nous permet d'attendre plusieurs tampons de fichiers en même temps. Zone de temps, réduisant le temps d'attente des IO et améliorant l'efficacité des IO du processus. La fonction

select() permet au programme de surveiller plusieurs descripteurs de fichiers et d'attendre qu'un ou plusieurs des descripteurs de fichiers surveillés soient "prêts". L'état dit « prêt » signifie : le descripteur de fichier n'est plus dans un état de blocage et peut être utilisé pour certains types d'opérations d'E/S, y compris les occurrences de lecture, d'écriture et d'exception

2.

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

ndfs

La valeur maximale du descripteur de fichier en attente de +1, par exemple : si le processus de candidature veut attendre les événements des descripteurs de fichier 3, 5, 8, alors

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

fd_set type

readfds Les types de writefds et exceptfds sont tous fd_set, alors quel est le type fd_set ?

  • Le type fd_set est essentiellement un bitmap La position du bitmap représente le descripteur de fichier correspondant, et le contenu indique si le descripteur de fichier est valide, 1 représente que le descripteur de fichier à cette position est valide, et 0. représente cette position. Le descripteur de fichier n'est pas valide.

  • Si les descripteurs de fichiers 2 et 3 sont définis dans le bitmap, le bitmap représente 1100.

  • La limite supérieure de fd_set est de 1024 descripteurs de fichiers.

readfds

  • readfds est une collection de descripteurs de fichiers en attente d'événements de lecture Si vous ne vous souciez pas des événements de lecture (le tampon contient des données), vous pouvez transmettre la valeur NULL.

  • Le processus d'application et le noyau peuvent définir readfds. Le processus d'application définit readfds pour informer le noyau d'attendre l'événement de lecture du descripteur de fichier dans readfds. Le

    kernel définit readfds pour indiquer au processus d'application qui a lu. les événements prendront effet

Quelle est la raison pour laquelle Linux utilise select ?

writefds

Semblable à readfds, writefds est une collection en attente d'événements d'écriture (qu'il y ait de l'espace dans le tampon). Si vous ne vous souciez pas des événements d'écriture, vous). peut passer la valeur NULL.

sauffds

Si le

noyau attend que le descripteur de fichier correspondant reçoive une exception

, alors définissez le descripteur de fichier ayant échoué dans exceptfds Si vous ne vous souciez pas des événements d'erreur, vous pouvez transmettre la valeur NULL. .

timeout

Définissez les blocs de sélection de temps dans le noyau. Si vous souhaitez le définir sur non bloquant, définissez-le sur NULL. Si vous souhaitez sélectionner un blocage pendant 5 secondes, vous créerez un

struct timeval time={5,0}

;où le type de structure de struct timeval est :

           struct timeval {
               long    tv_sec;         /* seconds */
               long    tv_usec;        /* microseconds */
           };

Valeur de retour

    S'il y a il n'y a pas de description de fichier Renvoie 0 lorsque le symbole est prêt ;
  • Si l'appel échoue, renvoie -1 ;
  • Le flux de travail de 3.select

Le processus de candidature

et le noyau doivent obtenir des informations de readfds et writefds. Parmi eux, le noyau doit savoir de readfds et writefds quels descripteurs de fichiers doivent attendre. , et le processus de candidature a besoin de savoir quels événements de descripteur de fichier sont prêts à partir de readfds et writefds.

Si nous voulons interroger et attendre en permanence les descripteurs de fichiers, le processus de candidature doit constamment réinitialiser readfds et writefds, car à chaque fois, sélectionnez. est appelé, le noyau modifiera readfds et writefds, nous devons donc définir un tableau dans l'

application

pour enregistrer les descripteurs de fichiers que le programme doit attendre Quelle est la raison pour laquelle Linux utilise select ?Assurez-vous que lorsque select est appelé, les valeurs dans. readfds et writefds seront les suivants :

4.Select服务器

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

一张图看懂select服务器:

Quelle est la raison pour laquelle Linux utilise select ?

简易版的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);    
W>    }    
      
      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){
W>        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){
W>        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();                                                                                                   
}

测试:

Quelle est la raison pour laquelle Linux utilise select ?

Quelle est la raison pour laquelle Linux utilise select ?

5.Select的缺陷

  • 由于fd_set的上限是1024,所以select能等待的读事件的文件描述符和写事件的文件描述是有上限的,如果作为一个大型服务器,能够同时链接的客户端是远远不够的。

  • 每次应用进程调用一次select之前,都需要重新设定writefds和readfds,如果进行轮询调用select,这对影响cpu效率。

  • 内核每一次等待文件描述符 都会重新扫描所有readfds或者writefds中的所有文件描述符,如果有较多的文件描述符,则会影响效率。

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer