阻塞io,在核心將資料準備好之前,系統會一直在等待,所有的套接字,預設都是阻塞方式
非阻塞io:如果核心還沒有將資料準備好,系統會呼叫仍然直接返回,並且返回ewouldblock錯誤碼,非阻塞io需要程式設計師迴圈的方式反覆嘗試讀寫檔案描述符,看資料是否準備好,這個過程稱為輪詢,,這對cpu來說是很大的浪費
訊號驅動io:核心將資料準備好的時候,使用sigio訊號通知應用程式進行io操作
io多路轉接:阻塞式的燈,但同時等待多個檔案描述符的就緒狀態
非同步io:由核心在資料拷貝完成時,通知應用程式拷貝完成
任何io過程中,都包含兩步:第一步是等待,第二步是資料拷貝,實際應用中,等待消耗的時間往往都大於資料拷貝的時間,如果我們要提高io的效率,就需要將等待的比重降低,讓等待的時間盡量少。概念
select系統呼叫是用來讓程式監視多個檔案描述符的狀態變化;程式停在select處等待,直到被監視的檔案描述符有乙個或者多個的狀態發生了變化。
函式原型:
#include
intselect(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
引數:
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的全部位
函式返回值:
執行成功則返回檔案描述詞狀態已改變的個數
返回0則代表在描述此狀態改變前已超過timeout時間,沒有返回
當有錯誤發生時則返回-1,錯誤原意存於errno,此時引數readfds, writefds, exceptfds和timeout的值不可**
//常用**段:
fd_set readset;
fd_set(fd, &readset);
select(fd+
1, &readset, null, null, null);
if(fd_isset(fd, readset))
select的執行過程
舉乙個例子,假如fd_set的長度為1個位元組,其中每乙個位元位表示乙個檔案描述符
執行fd_set set; fd_zero(&set); set8位表示為0000, 0000
若fd=5,執行fd_set,set變為0001, 0000
若再加入fd=2,fd=1,則set變為0001, 0011
執行select(6, &set, 0, 0, 0, 0)阻塞等待
若fd=1,fd=2上都發生了可讀事件,則select返回,set變為0000, 0011(fd=5沒有發生事件被清空)
至此,我們已經大致明白了select的執行過程和select函式的使用方法,那麼什麼樣的事件是讀就緒,什麼事件是寫就緒呢?
讀就緒:
socket核心中,接受緩衝區中的位元組數,大於等於低水位標記so_rcvlowat,此時可以無阻塞的讀該檔案描述符,並且返回值大於0
socket tcp通訊中,對端關閉連線,此時對該socket讀,則返回0;
監聽的socket上有新的連線請求
socket上有未處理的錯誤
寫就緒socket核心中,傳送緩衝區中的可用位元組數(空閒位置大小),大於等於低水位標記so_sndlowat,此時可以無阻塞的寫該檔案描述符,並且返回值大於0
socket的寫操作被關閉,對乙個寫操作被關閉的socket進行寫操作,會觸發sigpipe訊號;
socket使用非阻塞connect連線成功或失敗後
socket上有未讀取的錯誤
異常就緒
socket上收到帶外資料
總結select的特點
可監控的檔案描述符個數有限,取決於sizeof(fd_set),我的電腦上sizeof(fd_set)=128,也就是說最多可以支援128*8個檔案描述符select的缺點需要乙個資料結構array儲存select監控集中的fd
(1)select返回後,array作為源資料,遍歷陣列中的每乙個資料,進行fdisset判斷是不是在fd_set中
(2)select返回後會把以前假如的但沒喲事件發生的fd清空,所以每次開始select前都要重新從array**那個取得fd逐一加入,同時還要取得fd最大值maxfd,用於select的第乙個引數。
雖然有很多缺點,但是相比於多程序、多執行緒版本,其效果還是非常高的。
每次呼叫select,需要手動設定fd集合,介面的使用角度來看不方便
每次呼叫select,需要把fd集合從使用者態拷貝到核心態,這個開銷在fd很多時很大
每次呼叫select,都要在核心遍歷傳遞進來的所有fd,這個開銷在fd很多時很大
select支援的檔案描述符量太少
// 僅將客戶端傳送的訊息回顯,並沒有做其他處理
#include
#include
#include
#include
#include
#include
#include
#include
#define max_fds sizeof(fd_set)*8
#define init_data -1
static
void initarray(int arr, int num)
}return -1;
}int setarraytofdset(int arr, int num, fd_set* rfds)
}return max_fd;
}int startup(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_addr.s_addr = htonl(inaddr_any);
local.sin_port = htons(port);
if(bind(sock, (struct sockaddr*)&local, sizeof(local)) < 0)
if(listen(sock, 5) < 0)
return sock;
}static
void serviceio(int arr, int num, fd_set* rfds)
printf("get a new client [%s:%d]\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port));
if(addsocktoarray(arr, num, sock) == -1)
}else
if(i != 0 && fd_isset(arr[i], rfds))
else
if(s == 0)
else
}else}}
}int main(int argc, char* argv)
int listen_sock = startup(atoi(argv[1]));
int fd_array[max_fds];
initarray(fd_array, max_fds);
addsocktoarray(fd_array, max_fds, listen_sock);
fd_set rfds;
for(;;)
;switch(select(max_fd+1, &rfds, null, null, null))
}return
0;}
I O多路轉接之select
什麼是i o多路轉接技術 先構建一張有關描述符的列表,然後呼叫乙個函式,直到這些描述符中的乙個已準備好進行i o時,該函式才返回,在返回時,他告訴程序哪些描述符已準備好可以進行i o。上述呼叫的函式,有select,poll,pselect及poll的增強版epoll等,本文主要介紹select。函...
I O多路轉接之select
下面的巨集提供了處理fd set的這三種描述集的方式 fd clr inr fd,fd set set 用來清除描述片語set中相關fd 的位 fd isset int fd,fd set set 用來測試描述片語set中相關fd 的位是否為真 fd set int fd,fd set set 用來...
IO多路轉接之select
系統提供select函式來實現多路轉接。呼叫select函式介面的特點 一次需要等待多個檔案描述符。select函式原型 include include include int select int nfds,fd set readfds,fd set writefds,fd set exceptf...