IO多路復用大揭秘 徹底搞懂poll函式

2021-09-27 05:57:44 字數 4505 閱讀 4035

對於poll函式工作過程的理解

poll函式工作原理與select函式類似,也是監管一系列的檔案描述符,

看這些檔案描述符是否可讀/可寫/異常,再去呼叫io函式讀寫

不過poll函式沒有監管檔案描述符個數的限制 ,

與 select() 在本質上沒有多大差別,管理多個描述符也是進行輪詢,根據描述符的狀態進行處理。

poll函式的優缺點

1、優點

(1)poll() 不要求開發者計算最大檔案描述符加一的大小。 

(2)poll() 在應付大數目的檔案描述符的時候速度更快,相比於select。 

(3)它沒有最大連線數的限制,原因是它是基於鍊錶來儲存的。 

(4)在呼叫函式時,只需要對引數進行一次設定就好了

2、缺點

(1)大量的fd的陣列被整體複製於使用者態和核心位址空間之間,而不管這樣的複製是不是有意義。 

(2)與select一樣,poll返回後,需要輪詢pollfd來獲取就緒的描述符,這樣會使效能下降 

(3)同時連線的大量客戶端在一時刻可能只有很少的就緒狀態,因此隨著監視的描述符數量的增長,其效率也會線性下降

第一步

伺服器用socket建立連線套接字 呼叫listen函式監聽該套接字

(因為一開始只有乙個連線套接字 交給poll監聽 後面每個客戶端呼叫connect 到這個連線套接字的時候 )

(在伺服器端通過accept會建立針對這個客戶端的讀寫套接字 然後把這個讀寫套接字加入到poll的監聽列表)

(也就是說poll一開始只監聽我們上面建立的這個連線套接字,到後面有客戶端連線的時候會監聽乙個連線套接字和多個讀寫套接字)

第二步

poll函式的使用與select函式類似

poll函式問你:我要監管哪些套接字? 就是把你要監聽的套接字加入它的監聽列表

這一步實際上也是對poll函式的引數初始化的乙個過程,poll函式長這樣

#include int poll(struct pollfd *fds, nfds_t nfds, int timeout);

(1)fds:指向乙個結構體陣列的第0個元素的指標,每個陣列元素都是乙個struct pollfd結構,

用於指定測試某個給定的fd的條件

(2)nfds:表示fds結構體陣列的長度

(3)timeout:表示poll函式的超時時間,單位是毫秒

函式功能:

監視並等待多個檔案描述符的屬性變化

函式返回值:

(1)返回值小於0,表示出錯

(2)返回值等於0,表示poll函式等待超時

(3)返回值大於0,表示poll由於監聽的檔案描述符就緒返回,並且返回結果就是就緒的檔案描述符的個數。

與select採用位陣列然後用fd_set函式的形式不同,

poll函式中第乙個引數引入了 pollfd這個結構體的陣列,

陣列中每乙個元素就表示了,要監管的套接字,下面看看這個結構體具體長啥樣

pollfd結構體的原型為:

struct pollfd ;

通過給pollfd結構的的fd成員賦值,同時的設定好events引數,

表示就把這個檔案描述符加到poll函式的監管列表去了.

與select函式不同的一點就是對不同檔案描述符監管事件的確定方式不同.

select採用了三個型別的位陣列分別表示哪些是可讀是啟用的檔案描述符,

哪些是可寫時候啟用,哪些是出異常的時候啟用

poll函式用了events這個引數,在指定單個檔案描述符的時候就指定要監管的事件(可讀,可寫還是異常)

下面介紹一下給events引數賦值的巨集有哪些 

a. pollrdnorm(普通資料可讀)、pollrdband(優先順序帶資料可讀)和pollwrnorm(普通資料可寫)、pollwrband(優先順序帶資料可寫)將pollin(資料可讀)和pollout(資料可寫)劃分得更明顯,

以區分優先順序帶資料和普通資料,但是linux並不完全支援。 一般用pollin,pollout,pollerr就好

b. 可同時設定乙個檔案描述符的多個監管事件,用按位或運算子連線就好了

poll函式的其他兩個引數和select函式是類似的,

nfds表示監管的檔案描述符的個數

timeout 毫秒級等待:-1表示阻塞等,#define inftim,0表示立即返回,不阻塞程序

>0表示等待指定毫秒數,如當前系統時間精度不夠毫秒,向上取值

如果不再監控某個檔案描述符時,可以把pollfd中,fd設定為-1,poll不再監控此pollfd,下次返回時,把revents設定為0。

第三步:

poll函式開始工作,阻塞的開始查詢這些檔案描述符

並在設定的超時時間返回結果 看是不是有套接字是就緒的?

呼叫poll函式後,若發生錯誤則返回-1,若沒有套接字可以讀寫則返回0,

否則返回可以讀寫的套接字的個數,

大於0 表示有幾個套接字是可以讀寫的,那麼怎麼知道是哪幾個呢? 看第四步

第四步:

如果有套接字是就緒的,你就會問select函式:這些就緒的套接字裡面有沒有我想要讀寫的套接字

select函式使用fd_isset函式判斷某個套接字是否是啟用狀態

poll函式使用前面提到的pollfd結構體中的revents引數,revents變數在每一次poll函式呼叫完成後

核心設定會設定revents的值,這個值其實也就是上面列出來的那些events的巨集,

以說明對該描述符發生了什麼事件

比如 呼叫完poll函式後要檢視某乙個檔案描述符是否處於啟用狀態(比如可讀) 

是通過呼叫pollfd引數的revents引數與pollin做比較如果相等,則說明該檔案描述符處現在是可讀的

使用if語句:if(poll_fd.revents==pollin)

#include #include #include #include /* see notes */

#include #include #include #include #include #include #include #include #include #include extern int errno;

#define maxconnectnum (5)

int main()

; int flags = 0;

int nmaxfd = -1;

int i = 0;

static int s_ncountclient = 0;

struct pollfd stupollfd[maxconnectnum+1];

nlistenfd = socket( domain, type, protocol);

if(nlistenfd < 0)

memset(&addr_in, 0, sizeof(struct sockaddr_in));

addr_in.sin_family = af_inet;

addr_in.sin_port = htons(port);//htons的返回值是16位的網路位元組序整型數 htons尾的字母s代表short

addr_in.sin_addr.s_addr = htonl(inaddr_any);

ret = bind(nlistenfd, ( struct sockaddr * )(&addr_in), sizeof(struct sockaddr_in));

if(ret < 0)

ret = listen(nlistenfd, backlog);

if(ret < 0)

nmaxfd = 1;

memset(stupollfd, 0, sizeof(stupollfd));

stupollfd[0].fd = nlistenfd;

stupollfd[0].events |= pollin;

for(i = 1; i <= maxconnectnum; i++)

while(1)

printf("\n i[%d] fd[%d] chbuffer[%s] \n", i, stupollfd[i].fd , chbuffer);}}

if(pollin & stupollfd[0].revents)

if(s_ncountclient >= maxconnectnum)

printf("\n new client nnewclientfd[%d]\n",nnewclientfd);

s_ncountclient++;

for(i = 1; i <= maxconnectnum; i++)

break;}}

}} else if(num == 0)

else

} for(i = 1; i <= maxconnectnum; i++) }

close(stupollfd[0].fd);

return 0;

}

I O多路復用

一 五種i o模型 1 阻塞i o模型 最流行的i o模型是阻塞i o模型,預設情形下,所有套介面都是阻塞的。我們以資料報套介面為例來講解此模型 我們使用udp而不是tcp作為例子的原因在於就udp而言,資料準備好讀取的概念比較簡單 要麼整個資料報已經收到,要麼還沒有。然而對於tcp來說,諸如套介面...

i o多路復用

最常見的i o多路復用就是 select poll epoll了,下面說說他們的一些特點和區別吧。select 可讀 可寫 異常三種檔案描述符集的申明和初始化。fd set readfds,writefds,exceptionfds fd zero readfds fd zero writefds ...

I O多路復用

我們都知道unix like 世界裡,一切皆檔案,而檔案是什麼呢?檔案就是一串二進位製流而已,不管socket,還是fifo 管道 終端,對我們來說,一切都是檔案,一切都是流。在資訊 交換的過程中,我們都是對這些流進行資料的收發操作,簡稱為i o操作 input and output 往流中讀出資料...