I O多路復用之select poll

2021-08-05 18:44:35 字數 4264 閱讀 7327

系統提供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復用的 介質 是程序...