Mengapa menggunakan pilih dalam linux

青灯夜游
青灯夜游asal
2023-01-30 19:04:541300semak imbas

Oleh kerana pilih membenarkan pembangun menunggu beberapa penimbal fail pada masa yang sama, ia boleh mengurangkan masa menunggu IO dan meningkatkan kecekapan IO proses. Fungsi select() ialah fungsi pemultipleksan IO yang membolehkan program memantau berbilang deskriptor fail dan menunggu satu atau lebih deskriptor fail yang dipantau menjadi "sedia" yang dipanggil keadaan "bersedia" ialah Merujuk kepada: fail deskriptor tidak lagi disekat dan boleh digunakan untuk jenis operasi IO tertentu, termasuk boleh dibaca, boleh ditulis dan pengecualian.

Mengapa menggunakan pilih dalam linux

Persekitaran pengendalian tutorial ini: sistem linux7.3, komputer Dell G3.

select ialah fungsi komputer yang terletak dalam fail pengepala #include . Fungsi ini digunakan untuk memantau perubahan deskriptor fail—membaca, menulis atau pengecualian.

1. Pengenalan untuk memilih fungsi

Fungsi pilih ialah fungsi pemultipleksan IO adalah untuk menunggu acara dalam deskriptor fail siap. , pilih boleh membolehkan kami menunggu beberapa penimbal fail pada masa yang sama, mengurangkan masa menunggu IO dan meningkatkan kecekapan proses IO.

Fungsi select() membenarkan program memantau berbilang deskriptor fail dan menunggu satu atau lebih deskriptor fail yang dipantau menjadi "sedia". Keadaan yang dipanggil "sedia" bermaksud bahawa deskriptor fail tidak lagi disekat dan boleh digunakan untuk jenis operasi IO tertentu, termasuk boleh dibaca, boleh ditulis dan pengecualian

2 untuk memilih parameter fungsi

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

ndfs

Nilai maksimum deskriptor fail menunggu +1, sebagai contoh: proses permohonan mahu Tunggu untuk acara pada deskriptor fail 3, 5 dan 8, kemudian

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

jenis fd_set

readfds dan writefds, jenis exceptionfds kedua-duanya adalah fd_set, kemudian fd_set Apakah jenis?

  • jenis fd_set pada asasnya ialah bitmap , kedudukan bitmap mewakili fasa Kandungan deskriptor fail yang sepadan menunjukkan sama ada deskriptor fail sah 1 bermakna deskriptor fail di lokasi ini sah dan 0 bermakna deskriptor fail di lokasi ini tidak sah.
  • Jika deskriptor fail 2 dan 3 ditetapkan dalam peta bit, peta bit mewakili 1100.
  • Had atas fd_set ialah 1024 deskriptor fail.

readfds

  • readfds ialah set deskriptor fail menunggu acara baca ,. jika Jika anda tidak mengambil berat tentang acara baca (terdapat data dalam penimbal), anda boleh lulus nilai NULL.
  • Kedua-dua proses aplikasi dan kernel boleh menetapkan readfds Proses permohonan menetapkan readfds untuk memberitahu kernel untuk menunggu acara baca deskriptor fail. dalam readfds Dan kernel menetapkan readfds untuk memberitahu proses permohonan yang membaca acara berkuat kuasa

Mengapa menggunakan pilih dalam linux

writefds

Sama seperti readfds, writefds ialah koleksi menunggu acara tulis (is terdapat ruang dalam penimbal), jika anda tidak peduli Semasa menulis acara, anda boleh melepasi nilai NULL.

exceptfds

Jika pengecualian berlaku semasa kernel sedang menunggu deskriptor fail yang sepadan, maka akan Deskriptor fail yang gagal ditetapkan ke dalam exceptionfds Jika anda tidak mengambil berat tentang peristiwa ralat, anda boleh melepasi nilai NULL.

masa tamat

Tetapkan masa apabila pilih blok dalam kernel Jika anda ingin menetapkannya kepada tidak menyekat, tetapkannya kepada NULL. Jika anda ingin memilih untuk menyekat selama 5 saat, anda akan mencipta struct timeval time={5,0};

di mana jenis struktur struct timeval adalah :

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

Nilai pulangan

  • Mengembalikan 0 jika tiada deskriptor fail sedia
  • Mengembalikan -1 jika panggilan gagal;
  • Jika peristiwa berlaku dalam readfds dalam tamat masa, kembalikan baki masa tamat masa.

3.Pilih aliran kerja

Proses permohonan dan KernelKedua-duanya perlu mendapatkan maklumat daripada readfds dan writefds Antaranya, kernel perlu mengetahui deskriptor fail yang perlu menunggu dari readfds dan writefds, dan proses permohonan perlu mengetahui peristiwa deskriptor fail yang sedia dari readfds dan writefds. .

Mengapa menggunakan pilih dalam linux

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

Mengapa menggunakan pilih dalam linux

4.Select服务器

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

一张图看懂select服务器:

Mengapa menggunakan pilih dalam linux

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

测试:

Mengapa menggunakan pilih dalam linux

Mengapa menggunakan pilih dalam linux

5.Select的缺陷

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

推荐学习:Linux视频教程

Atas ialah kandungan terperinci Mengapa menggunakan pilih dalam linux. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Artikel sebelumnya:Untuk apa direktori akar linux?Artikel seterusnya:Untuk apa direktori akar linux?