select函數與I/O多路轉接__函數

來源:互聯網
上載者:User

轉自:http://blog.csdn.net/linyt/article/details/1722445

select函數與I/O多路轉接


相作大家都寫過讀寫IO操作的代碼,例如從socket中讀取資料可以使用如下的代碼:

while( (n = read(socketfd, buf, BUFSIZE) ) >0)

if( write(STDOUT_FILENO, buf, n) = n)

{

printf(“write error”);

exit(1);

}

當代碼中的socketfd描述符所對應的檔案表項是處於阻塞時,它會一直阻塞,直到有資料從網路的另一端發送過來。如果它是一個伺服器程式,它要讀寫大量的socket,那麼在某一個socket上的阻塞很明顯會影響與其它socket的互動過程。類似的問題不單單出現在網路上,還可以出現在讀寫加鎖的檔案和FIFO等等一系列的情況。

一種比較好的解決方案似乎是採用非阻塞IO來實現。把所要讀取資料的socketfd設定為非阻塞狀態,依次用read函數檢查是否有資料到來,如有,它會返回接到資料的個數,否則它會返回-1以表示當前還沒有資料到達。這樣,對於每個socket,如有資料到來則讀取,沒有也會馬上返回。這就是非阻塞IO的好處拉。部分代碼如下:

//clientfd[] 為用戶端的socket描述符組數,假設數組的大小為MAX,並且所有用戶端socket描述符都設定為非阻塞狀態時。

for(i = 0; i < MAX; ++i)

{

int n;

if( (n = read(clientfd[i], buf, SZIE)) >0)

{

//send response to client in here.

}

}

這裡代碼看起來與上面的代碼沒有太大的區別,其實是有很大的區別;區別就是使使用了非阻塞IO進行整個互動過程,使得各個用戶端都得到相對平等的時間待遇。這種模式我們通常稱為這“輪詢”模式。輪詢模式同樣有它的不足之處,在執行read函數時,實際上大部分時間還是沒有資料可讀的,但仍不斷地執行read,浪費了很多CPU時間。


實際,對於上述的問題,一種比較好的技術就是I/O多路轉接(I/O multiplexing)。它可謂是上面兩種方法的接衷:先構造一張有關描述符的資料表,然後調用一個函數,僅當有一個或多個描述符已準備可以進行IO操作時才返回,否則一直阻塞。在返回時,它會告訴進程那些描述符已準備好可以進行IO。

現在實現多路轉接的任務落在select函數的身上了,現在給大家詳細介紹select函數的使用。我們的主角出場了,呵呵。掌聲。

函數的功能:實現多路轉接,通過調用核心來實現。它向核心提供如下的參數

1)我們所關心的描述符

2)對於每個描述符,我們所關心的條件(是否讀一個給定的描述符,還是想寫一個給定的描述符,還是關心一個描述符的異常條件)

3)希望等待多久時間(可以永遠等待,等待一個固定時間,或完全不等待)

從select返回時,核心告訴我們:

1)已準備好的描述符數量

2)哪一個描述符已準備好讀、寫或異常條件

使用這種傳回值,就可調用相應的I/O函數,通常是read或write,並確知該函數不會阻塞。


函數的定義:

#include <sys/types.h>

#include <sys/time.h>

#include <unistd.h>


int select( int maxfdp1, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, sturct timeval *tvptr);

返回:準備就緒的描述符,若逾時則為0,若出錯則為-1


最後一個參數為struct timeval的指標變數,它指定願意等待的時間。

struct timeval{

long tv_sec; /*秒數*/

long tv_usec; /*微秒數*/

};


對於參數tvptr有三種情況:

如果tvptr == NULL 則永遠等待。如果捕捉到一個訊號則中斷此無限期等待。當指定的描述符中的一個或多個已準備好或捕捉到一個訊號則返回。如果是捕捉到一個資訊,則select返回-1,errno設定為EINTR.

如果tvptr->sec ==0 && tvptr->tv_usec == 0 則完全不等待。即測試所有的描述符後馬上返回。這是得到多個描述符的狀態而不阻塞select函數的輪詢方法。

如果tvptr->tv_sec != 00 || tvptr->tv_usec != 0 則等待指定的秒數和微秒數。當指它的描述符之一已準備好,或指定的時間值已逾時則返回。如果在逾時時還沒有一個描述符準備好,則傳回值是0。與第一種情況類似,這種等待可能被訊號所中斷。


中間三個參數readfds, writefds, exceptfds是指向描述符集的指標,它們描述了我們關心的可讀、可寫和處異常條件的各個描述符。這種描述符集存在一種叫fd_set的資料類型中(在標頭檔select.h中有定義)。具體做法每個描述符對應於資料結構fd_set所佔用記憶體空間的一個位,如果第i位為0則表示值為i的描述符不包含在該集中,反之亦然。為了方便使用者使用,系統提供了如下的四個宏進行操作。

FD_ZERO(fd_set *fdset); //清空fdset中的所有位

FD_SET(int fd, fd_set *fdset); //在fdset中開啟fd所對應的位

FD_CLR(int fd, fd_set *fdset); //在fdset中關閉fd所對應的位

FD_ISSET(int fd, fd_set *fdset); //測試fd是否在fdset中


通常做法是,先定義一個描述符集

fd_set rset;

int fd;

必須使用FD_ZERO清除其所有位

FD_ZERO(&rset);

然後設定我們所關心的位

FD_SET(fd, &rset);

FD_SET(STDOUT_FILENO,&rset);

從select返回時,用FD_ISSET測試該集中的一個給定位是否仍舊設定

if( FD_ISSET(fd, &rset)){

...

}


select函數的這三個參中的任一個(或全部)可以是null 指標,這表示對相應的條件不關心。值得一提的是:如果這三個指標全部為空白,則select函數提供了比sleep更精確的計時器(sleep等待整數秒,而select函數可以等待少於1秒的時間,具體時間粒紋取決於系統時鐘)。

select第一個參數 maxfdp1的意思是“最大的fd加1(max fd plus 1)”。在三個描述符集中找出最大的描述符值,然後加1,這就是第一個參數。也可以將第一個參數設定為FD_SETSIZE,這是<sys/types.h>這義的一個常數,通常是256或1024。但對於大部分應用程式來說,此值太大了。如果將maxfdp1設定為最大的描述符值加1,核心只需要在此範圍內尋找開啟位,而不必在上數百個的大範圍內搜尋。

如下是範例程式碼:

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.