系統提供select函式用來實現i/o多路復用輸入/輸出模型。select系統呼叫是用來讓我們的程式監視多個檔案描述狀態變化的。程式會停在select這裡等待,直到被監視的檔案描述符有乙個或多個發生狀態變化。通常i/o操作有兩個步驟,乙個是等,另乙個是資料搬遷。select主要是在等的這個狀態阻塞著直到事件發生。
標頭檔案:
#include
#include
#include
#include
函式原型
int select(int nfds,fd_set *reads,fd_set *writefds,fd_set *exceptfds,struct timeval *timeout);
引數:
nfds:是需要監視的最大的檔案描述符的值+1。
fd_set:
fd_set底層是用位圖實現的,每乙個位都代表乙個檔案描述符。readfds,writefds,exceptfds分別對應於需要檢測的可讀檔案描述符結合,可寫描述符集合,異常檔案描述符集合,他們都是輸入輸出型引數。
當作為輸入引數時:只要檔案描述符集合中對應的位上為1,就表示select需要監視這個描述符的狀態。比如readfds裡面的檔案描述符就代表他們需要等待讀事件,writefds裡面的檔案描述符就代表他們需要等待寫事件。
當作為輸出引數時,只要檔案描述符集合中對應的位上為1,就代表他們等待的事件已經就緒,這是由核心設定的。
timeout:設定超時時間。
timeout裡面的成員設定為特定的時間值:
如果在這段時間裡面沒有事件發生,select將超時返回。struct timeval結構用於描述一段時間長度,如果在這個時間內,需要監聽的描述符沒有事件發生則函式返回,返回值為0。
struct timeval
timeout裡面的成員等於0:表示非阻塞輪詢方式,不斷的去檢測描述符集合的狀態,然後立即返回。
timeout為null:表示以阻塞的方式等待事件發生。
返回值:
成功的話,返回檔案描述符狀態已改變的個數。如果返回0代表在描述符狀態改變之前已經超過timeout時間。如果有錯誤發生的話,則返回-1。
下面的巨集用來處理描述符集合:
void fd_clr(int fd,fd_set* set); 用來清除描述符片語set中相關fd的位。
int fd_isset(int fd,fd_set* set); 用來測試set中相關fd的位是否為真。
void fd_set(int fd,fd_set* set); 用來設定描述片語set中相關fd的位。
void fd_zero(fd_set *set);用來清除描述符片語set的全部位。
select模型:
select可監控的描述符取決於sizeof(fd_set)的值,因為檔案描述符是用位圖表示的,所以能監控的描述符的最大數量是sizeof(fd_set)*8,fd_set的大小可以調整。
將fd加入select監控集的同時,還要使用乙個額外的陣列儲存select監控集中的fd。一方面是用於在select返回後,array作為源資料和fd_set進行fd_isset判斷事件是否就緒。另一方面是select返回之後會把以前加入的但並無事件發生fd清空,這是由核心清空的,所以每次開始select前都要重新從array中取得fd加入到fd_set中。
還有就是因為select第乙個引數是當前要監測的檔案描述符的最大值加1,可以在掃瞄array的同時取得fd的最大值maxfd,用於select的第乙個引數。
所以select的缺點就是,每次selcet之前都要遍歷陣列加入fd,select返回後還要遍歷陣列進行判斷哪些事件已經就緒(fd_isset判斷是否有事件發生)。
select的缺點:
1、每次呼叫select,都需要把fd集合從使用者態拷貝到核心態。這個開銷在fd很多的時候會很大。
2、select在返回之後,需要我們遍歷陣列去查詢事件就緒的描述符。這個過程的時間複雜度是o(n)。而epoll它查詢就緒事件的時候是o(1)。
3、select支援的檔案描述符的數量太小了,預設是1024。
總結:針對select的缺點來看,即時fd_set可以改動,也不建議將它改的很大,因為一但支援的檔案描述多了,效率自然也就降低了。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define size 128
int startup(char *ip,int port)
int opt=1;
setsockopt(sock,sol_socket,so_reuseaddr,&opt,sizeof(opt));
struct sockaddr_in local;
local.sin_family=af_inet;
local.sin_port=htons(port);
local.sin_addr.s_addr=inet_addr(ip);
if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0)
if(listen(sock,5)<0)
return sock;
}int main(int argc,char* argv)
int lis_sock=startup(argv[1],atoi(argv[2]));
int gfds[size];
memset(gfds,-1,size*4);
fd_set rfds;
fd_zero(&rfds);
while(1)
;gfds[0]=lis_sock;
int max_fd=-1;
int i=0;
for(;iif(max_fdif(gfds[i]>=0)
}int ret=select(max_fd+1,&rfds,null,null,null);
switch(ret)
else
}if(k>=size)}}
int j=1;
for(;jif(fd_isset(gfds[j],&rfds))
else
if(s==0)
else}}
break;}}
return
0;}
poll也是一種i/o多路轉接的方式,select將三種事件進行了區分,並且用三個點陣圖來表示不同的監測事件。而poll統一用一種結構來管理要監測的事件。
#include
int poll(struct pollfd* fds,nfds_t nfds,int timeout);
引數:
fds:
它是乙個結構體陣列,其中元素的型別如下:
struct pollfd
pollfd結構裡面包含了要監視的event和已經發生的event。同時pollfd並沒有數量的限制,但是因為select和poll在返回之後,都需要輪詢來獲取就緒的描述符,所以當監視的檔案描述符很多的時候,poll的效能也會下降。fds裡面的每乙個元素都代表乙個要監測的事件。
nfds:表示陣列的長度,也就是要監測事件的個數。
timeout:設定超時時間。
返回值:成功的話返回就緒事件的個數。如果超時的話返回0,失敗的話返回-1。
poll支援的常見事件型別:
pollin:資料可讀
pollout:資料可寫
pollerr:錯誤
pollrdhup:tcp連線被對方關閉,或者對方關閉了寫操作,他由gnu引入。在使用的時候要加上#define _gnu_source。
``
**poll與select的比較:
1、select將讀寫異常的事件分開進行監測,poll將所有的事件型別都統一用一種結構體表示。
2、select的3個fd_set引數都是輸入輸出型引數,當輸入的時候表示要監測的檔案描述符,當輸出的時候表示就緒的檔案描述符,所以每一次select之前都要重新將要監測的檔案描述符設定進fd_set中。而poll用events表示要監測的檔案描述符,revents表示就緒的檔案描述符,所以poll只需要設定一次就可以了。
3、當事件就緒的時候,select和poll都要遍歷整個所有監測的檔案描述符來查詢就緒的事件。**
I O多路復用之poll
poll的優點 1 poll 不要求開發者計算最大檔案描述符加一的大小。2 poll 在應付大數目的檔案描述符的時候速度更快,相比於select。3 它沒有最大連線數的限制,原因是它是基於鍊錶來儲存的。poll的缺點 1 大量的fd的陣列被整體複製於使用者態和核心位址空間之間,而不管這樣的複製是不是...
I O多路復用之select
阻塞i o模型 應用程式呼叫乙個i o函式,應用程式會一直等待資料準備好。如果資料沒有準備好,就會一直等待。只有當資料準備好,從核心拷貝到使用者空間io函式才成功返回。非阻塞i o模型 把乙個套介面設定成非阻塞告訴核心,當所有的i o操作無法完成時,不要將程序睡眠,而返回乙個錯誤資訊。此時i o操作...
IO多路復用之select
1 背景知識 我們首先來看看伺服器程式設計的模型,客戶端發來的請求服務端會產生乙個程序來對其進行服務,每當來乙個客戶請求就產生乙個程序來服務,然而程序不可能無限制的產生,因此為了解決大量客戶端訪問的問題,引入了io復用技術。即 乙個程序可以同時對多個客戶請求進行服務。也就是說io復用的 介質 是程序...