select 関数を使用すると、開発者は複数のファイル バッファを同時に待機できるため、IO 待機時間が短縮され、プロセスの IO 効率が向上します。 select() 関数は、プログラムが複数のファイル記述子を監視し、監視されている 1 つ以上のファイル記述子が「準備完了」になるのを待機できるようにする IO 多重化関数です。いわゆる「準備完了」状態とは、ファイルを指します。記述子はブロックされなくなり、読み取り可能、書き込み可能、例外を含む特定の種類の IO 操作に使用できるようになりました。
#include
select 関数は IO 多重化関数であり、その主な機能はファイル記述子のイベントの準備が完了するのを待つことです。 , select を使用すると、複数のファイル バッファーを同時に待機できるようになり、IO 待機時間が短縮され、プロセスの IO 効率が向上します。
select() 関数を使用すると、プログラムは複数のファイル記述子を監視し、監視されている 1 つ以上のファイル記述子が「準備完了」になるまで待機できます。いわゆる「準備完了」状態とは、ファイル記述子がブロッキング状態ではなくなり、読み取り可能、書き込み可能、例外の発生など、特定のタイプの IO 操作に使用できることを意味します
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
nfds=max(3,5,8)+1;
fd_set タイプは本質的にビットマップ です。ビットマップの位置は対応するファイル記述子を示し、内容はファイル記述子が有効かどうかを示します。1 はファイル記述子を表しますその場所のファイル記述子は有効です。0 はその場所のファイル記述子が無効であることを意味します。
を待機しているファイル記述子の コレクションです。 if 読み取りイベントを気にしない場合 (バッファーにデータがある場合)、NULL 値を渡すことができます。
カーネル readfds の設定はどの読み取りイベントが有効になるかをアプリケーション プロセスに指示するためのものです
失敗したファイル記述子をExceptfdsに設定します。 、エラー イベントを気にしない場合は、値 NULL を渡すことができます。 timeout
struct timeval の構造タイプは次のとおりです:
struct timeval { long tv_sec; /* seconds */ long tv_usec; /* microseconds */ };
戻り値
kernel の両方で readfds と writefds が必要ですその中で、カーネルは、どのファイル記述子が readfds と writefds から待機する必要があるかを知る必要があり、アプリケーション プロセスは、どのファイル記述子イベントが readfds と writefds から準備ができているかを知る必要があります。 ファイル記述子を継続的にポーリングして待機する場合、アプリケーション プロセスは常に readfds と writefds をリセットする必要があります。これは、select が呼び出されるたびに、カーネルが readfds と writefds を変更するため、 Application
Medium配列
を設定して、プログラムが待機する必要があるファイル記述子を保存します。selected が呼び出されたときに、readfds と writefds が次のようになることを確認します。
如果是一个select服务器进程,则服务器进程会不断的接收有新链接,每个链接对应一个文件描述符,如果想要我们的服务器能够同时等待多个链接的数据的到来,我们监听套接字listen_sock读取新链接的时候,我们需要将新链接的文件描述符保存到read_arrys数组中,下次轮询检测的就会将新链接的文件描述符设置进readfds中,如果有链接关闭,则将相对应的文件描述符从read_arrys数组中拿走。
一张图看懂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,'\0',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]='\0'; 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(); }
测试:
由于fd_set的上限是1024,所以select能等待的读事件的文件描述符和写事件的文件描述是有上限的,如果作为一个大型服务器,能够同时链接的客户端是远远不够的。
每次应用进程调用一次select之前,都需要重新设定writefds和readfds,如果进行轮询调用select,这对影响cpu效率。
内核每一次等待文件描述符 都会重新扫描所有readfds或者writefds中的所有文件描述符,如果有较多的文件描述符,则会影响效率。
以上がLinux が select を使用する理由は何ですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。