久久精品人人爽,华人av在线,亚洲性视频网站,欧美专区一二三

linux要用select的原因是什么

280次閱讀
沒有評論

共計 5036 個字符,預計需要花費 13 分鐘才能閱讀完成。

本篇內容介紹了“linux 要用 select 的原因是什么”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓丸趣 TV 小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!

因為 select 可以使開發者在同時等待多個文件緩沖區,可減少 IO 等待的時間,能夠提高進程的 IO 效率。select()函數是 IO 多路復用的函數,允許程序監視多個文件描述符,等待所監視的一個或者多個文件描述符變為“準備好”的狀態;所謂的”準備好“狀態是指:文件描述符不再是阻塞狀態,可以用于某類 IO 操作了,包括可讀,可寫,發生異常三種。

select 是一個計算機函數,位于頭文件 #include sys/select.h。該函數用于監視文件描述符的變化情況——讀寫或是異常。

1. select 函數介紹

select 函數是 IO 多路復用的函數,它主要的功能是用來等文件描述符中的事件是否就緒,select 可以使我們在同時等待多個文件緩沖區,減少 IO 等待的時間,能夠提高進程的 IO 效率。

select()函數允許程序監視多個文件描述符,等待所監視的一個或者多個文件描述符變為“準備好”的狀態。所謂的”準備好“狀態是指:文件描述符不再是阻塞狀態,可以用于某類 IO 操作了,包括可讀,可寫,發生異常三種

2. select 函數參數的介紹

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

ndfs

等待的文件描述符的最大值 +1,例如:應用進程想要去等待文件描述符 3,5,8 的事件,則

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

fd_set 類型

readfds 和 writefds,exceptfds 的類型都是 fd_set, 那么 fd_set 類型是什么呢?

fd_set 類型本質是一個位圖,位圖的位置 表示 相對應的文件描述符,內容表示該文件描述符是否有效,1 代表該位置的文件描述符有效,0 則表示該位置的文件描述符無效。

如果將文件描述符 2,3 設置位圖當中,則位圖表示的是為 1100。

fd_set 的上限是 1024 個文件描述符。

readfds

readfds 是 等待讀事件的文件描述符集合,. 如果不關心讀事件(緩沖區有數據),則可以傳 NULL 值。

應用進程和內核都可以設置 readfds,應用進程設置 readfds 是為了通知內核去等待 readfds 中的文件描述符的讀事件. 而 內核設置 readfds 是為了告訴應用進程哪些讀事件生效

writefds

與 readfds 類似,writefds 是等待寫事件 (緩沖區中是否有空間) 的集合,如果不關心寫事件,則可以傳值 NULL。

exceptfds

如果內核等待相應的文件描述符發生異常,則將失敗的文件描述符設置進 exceptfds 中,如果不關心錯誤事件,可以傳值 NULL。

timeout

設置 select 在內核中阻塞的時間,如果想要設置為非阻塞,則設置為 NULL。如果想讓 select 阻塞 5 秒,則將創建一個 struct timeval time={5,0};

其中 struct timeval 的結構體類型是:

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

返回值

如果沒有文件描述符就緒就返回 0;

如果調用失敗返回 -1;

如果 timeout 中中 readfds 中有事件發生,則返回 timeout 剩下的時間。

3.select 的工作流程

應用進程和內核都需要從 readfds 和 writefds 獲取信息,其中,內核需要從 readfds 和 writefds 知道哪些文件描述符需要等待,應用進程需要從 readfds 和 writefds 中知道哪些文件描述符的事件就緒.

如果我們要不斷輪詢等待文件描述符,則應用進程需要不斷的重新設置 readfds 和 writefds,因為每一次調用 select,內核會修改 readfds 和 writefds,所以我們需要在 應用程序 中 設置一個數組 來保存程序需要等待的文件描述符,保證調用 select 的時候 readfds 和 writefds 中的將如下:

4.Select 服務器

如果是一個 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(); }

測試:

5.Select 的缺陷

由于 fd_set 的上限是 1024,所以 select 能等待的讀事件的文件描述符和寫事件的文件描述是有上限的,如果作為一個大型服務器,能夠同時鏈接的客戶端是遠遠不夠的。

每次應用進程調用一次 select 之前,都需要重新設定 writefds 和 readfds,如果進行輪詢調用 select,這對影響 cpu 效率。

內核每一次等待文件描述符 都會重新掃描所有 readfds 或者 writefds 中的所有文件描述符,如果有較多的文件描述符,則會影響效率。

“linux 要用 select 的原因是什么”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注丸趣 TV 網站,丸趣 TV 小編將為大家輸出更多高質量的實用文章!

正文完
 
丸趣
版權聲明:本站原創文章,由 丸趣 2023-07-12發表,共計5036字。
轉載說明:除特殊說明外本站除技術相關以外文章皆由網絡搜集發布,轉載請注明出處。
評論(沒有評論)
主站蜘蛛池模板: 剑川县| 东丰县| 黑龙江省| 正镶白旗| 淮滨县| 阿克苏市| 漳平市| 五华县| 河北省| 鄂尔多斯市| 夏河县| 南城县| 交口县| 板桥市| 吉首市| 新郑市| 昌江| 调兵山市| 连平县| 抚远县| 合山市| 枣庄市| 原平市| 徐州市| 新巴尔虎右旗| 彭山县| 阿瓦提县| 繁峙县| 从江县| 互助| 汉川市| 景东| 盱眙县| 绍兴县| 中方县| 大同县| 和龙市| 龙胜| 黑龙江省| 克什克腾旗| 六安市|