IO多路復用 select

2021-08-19 03:14:09 字數 3401 閱讀 7442

select系統呼叫的目的是:在一段指定時間內,監聽使用者感興趣的檔案描述符上的可讀、可寫和異常事件。poll和select應該被歸類為這樣的系統 呼叫,它們可以阻塞地同時探測一組支援非阻塞的io裝置,直至某乙個裝置觸發了事件或者超過了指定的等待時間——也就是說它們的職責不是做io,而是幫助 呼叫者尋找當前就緒的裝置。

#include #include int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
引數nfds是需要監視的檔案描述符數,要監視的檔案描述符值為0 ~ nfds-1。

引數readfds指定需要監視的可讀檔案描述符集合,當這個集合中的乙個描述符上有資料到達時,系統將通知呼叫select函式的程式。

引數writefds指定需要監視的可寫檔案描述符集合,當這個集合中某個描述符可以傳送資料時,程式將收到通知。

引數exceptfds指定需要監視的異常檔案描述符集合,當這個集合中的乙個描述符發生異常時,程式將受到通知。

引數timeout指定了阻塞的時間,如果在這段時間內監視的檔案描述符上沒有事件發生,則函式select()將返回0。

struct fd_set可以理解為乙個集合,這個集合中存放的是檔案描述符,可通過以下四個巨集進行設定:

void fd_zero(fd_set *fdset);           //清空集合

void fd_set(int fd, fd_set *fdset); //將乙個給定的檔案描述符加入集合之中

void fd_clr(int fd, fd_set *fdset); //將乙個給定的檔案描述符從集合中刪除

int fd_isset(int fd, fd_set *fdset); // 檢查集合中指定的檔案描述符是否可以讀寫

fd_set結構體是檔案描述符集,該結構體實際上是乙個整型陣列,陣列中的每個元素的每一位標記乙個檔案描述符。fd_set能容納的檔案描述符 數量由fd_setsize指定,一般情況下,fd_setsize等  於1024,這就限制了select能同時處理的檔案描述符的總量。

timeout告知核心等待所指定描述字中的任何乙個就緒可花多少時間。其timeval結構用於指定這段時間的秒數和微秒數。

struct timeval;

select()返回值情況: 

超時時間內,如果檔案描述符就緒,select返回就緒的檔案描述符總數(包括可讀、可寫和異常),如果沒有檔案描述符就緒,select返回0; 

select呼叫失敗時,返回 -1並設定errno,如果收到訊號,select返回 -1並設定errno為eintr。

檔案描述符的就緒條件: 

在網路程式設計中, 

1)下列情況下socket可讀:

a) socket核心接收緩衝區的位元組數大於或等於其低水位標記so_rcvlowat; 

b) socket通訊的對方關閉連線,此時該socket可讀,但是一旦讀該socket,會立即返回0(可以用這個方法判斷client端是否斷開連線); 

c) 監聽socket上有新的連線請求; 

d) socket上有未處理的錯誤。 

2)下列情況下socket可寫:

a) socket核心傳送緩衝區的可用位元組數大於或等於其低水位標記so_sndlowat; 

b) socket的讀端關閉,此時該socket可寫,一旦對該socket進行操作,該程序會收到sigpipe訊號; 

c) socket使用connect連線成功之後; 

d) socket上有未處理的錯誤。

select優點

解決了讀寫阻塞的問題,一程序一線程就可完成資料間的傳輸,避免了多程序多執行緒的消耗,提高了資源利用率。

select缺點

select支援的檔案描述符數量最大1024

每次呼叫select都需要把fd集合從使用者態拷貝到核心態,這個開銷在fd很多時會很大

每次呼叫select都需要在核心遍歷傳遞進來的所有fd,檢視有沒有就緒的fd,這個開銷在fd很多時也很大,效率隨fd數目增加而線性下降

#include #include #include #include #include #include #include const int backlog = 8;  //最大監聽數

int fds[32];

int fds_nums = sizeof(fds) / sizeof(*fds);

#define buffsize 1024

int m_listen(char *ip, int port)

//設定套接字屬性

int optval = 1;

if(setsockopt(sock_fd, sol_socket, so_reuseaddr, (void *)&optval, sizeof(int)) == -1)

//繫結埠

if(bind(sock_fd, (struct sockaddr*)&ser, sizeof(struct sockaddr)) == -1)

//監聽

if(listen(sock_fd, backlog) == -1)

return sock_fd;

}int main(int argc, char *argv)

int sock_fd = m_listen(argv[1], atoi(argv[2]));

fd_set readfds;

//清空讀取檔案描述符集

fd_zero(&readfds);

int i;

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

fds[i] = -1;

//將監聽套接字加入0號位

fds[0] = sock_fd;

while(1)

switch(select(max_fd + 1, &readfds, null, null, null))

fprintf(stderr, "accept a new client, ip:%s port:%d\n", inet_ntoa(cli.sin_addr),\

ntohs(cli.sin_port));

int j;

for(j = 0; j < fds_nums; ++j)

if(fds[j] == -1)

if(j == fds_nums)

close(conn_fd);

}else

;if((ret = recv(fds[i], buff, buffsize - 1, 0)) == -1)

else if(ret)

else}}

}break;}}

return 0;

}

IO多路復用 select

出自朱有鵬老師的課堂 include include include include include include include include include int main void 當前有兩個fd,乙個是fd乙個是0 處理 myset fd zero myset 全部清零 fd set ...

I O多路復用 select

int select int n,fd set readfds,fd set writefds,fd set exceptfds,struct timeval timeout 有三種型別的描述符型別 readset writeset exceptset,分別對應讀 寫 異常條件的描述符集合。fd s...

IO多路復用 select模型

客戶端 見 c s通訊 伺服器阻塞型使用 伺服器端 include include include include include include include include include include include include int main set local address m...