Unix下分5種基本的I/O模型:
1.阻塞I/O
2.非阻塞I/O
3.I/O複用(select和poll)
4.信號驅動I/O(SIGIO)
5.異步I/O(POSIX.1的aio_系列函數)
2.非阻塞I/O
3.I/O複用(select和poll)
4.信號驅動I/O(SIGIO)
5.異步I/O(POSIX.1的aio_系列函數)
Unix中一個輸入操作一般有兩個不同的階段:
1.等待數據準備好。
2.從內核到進程拷貝數據。
對於一個sockt上的輸入操作,第一步一般是等待數據到達網絡,當分組到達時,它被拷貝到內核中的某個緩衝區,第二步是將數據從內核緩衝區拷貝到應用程序緩衝區。
1.等待數據準備好。
2.從內核到進程拷貝數據。
對於一個sockt上的輸入操作,第一步一般是等待數據到達網絡,當分組到達時,它被拷貝到內核中的某個緩衝區,第二步是將數據從內核緩衝區拷貝到應用程序緩衝區。
下面分別介紹上面提到的5類I/O模型
本文中我們用UDP來進行舉例,並且我們將函數recvfrom視爲系統調用,這樣讓我們的注意力都集中在I/O模型上。
阻塞I/O模型
最流行的I/O模型是阻塞I/O模型,缺省時,所有sockt都是阻塞的,這意味着當一個sockt調用不能立即完成時,進程進入睡眠狀態,等待操作完成。如圖:
最流行的I/O模型是阻塞I/O模型,缺省時,所有sockt都是阻塞的,這意味着當一個sockt調用不能立即完成時,進程進入睡眠狀態,等待操作完成。如圖:
圖1 阻塞I/O模型
在圖1中,進程調用recvfrom,此調用直到數據報到達且拷貝到應用緩衝區或是出錯才返回。最常見的錯誤是系統調用被信號中斷,我們所說進程阻塞的整段時間是指從調用recvfrom開始到它返回的這段時間,當進程返回成功指示時,應用進程開始處理數據報。
下面是一個採用阻塞I/O模型編寫的簡單服務器端代碼(本代碼來至於Unix網絡編程),這段代碼的功能是把客戶端發來的數據再回射到客戶端,本例子中進程阻塞於recvfrom.
#include "unp.h"
void dg_echo(int sockfd, SA *pcliaddr, socklen_t clilen);
int
main(int argc, char **argv)
{
int sockfd;
struct sockaddr_in servaddr, cliaddr;
sockfd = Socket(AF_INET, SOCK_DGRAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
Bind(sockfd, (SA *) &servaddr, sizeof(servaddr));
dg_echo(sockfd, (SA *) &cliaddr, sizeof(cliaddr));
}
main(int argc, char **argv)
{
int sockfd;
struct sockaddr_in servaddr, cliaddr;
sockfd = Socket(AF_INET, SOCK_DGRAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
Bind(sockfd, (SA *) &servaddr, sizeof(servaddr));
dg_echo(sockfd, (SA *) &cliaddr, sizeof(cliaddr));
}
void dg_echo(int sockfd, SA *pcliaddr, socklen_t clilen)
{
int n;
socklen_t len;
char mesg[MAXLINE];
for ( ; ; ) {
len = clilen;
n = Recvfrom(sockfd, mesg, MAXLINE, 0, pcliaddr, &len);
Sendto(sockfd, mesg, n, 0, pcliaddr, len);
}
}
{
int n;
socklen_t len;
char mesg[MAXLINE];
for ( ; ; ) {
len = clilen;
n = Recvfrom(sockfd, mesg, MAXLINE, 0, pcliaddr, &len);
Sendto(sockfd, mesg, n, 0, pcliaddr, len);
}
}
非阻塞I/O模型
當我們把一個sockt設置成非阻塞放式時,即通知內核:當請求的I/O操作非得讓進程睡眠才能完成時,不要讓進程睡眠,而應該返回一個錯誤。如圖:
當我們把一個sockt設置成非阻塞放式時,即通知內核:當請求的I/O操作非得讓進程睡眠才能完成時,不要讓進程睡眠,而應該返回一個錯誤。如圖:
圖2 非阻塞I/O模型
如圖2所示,前3次調用recvfrom時仍無數據返回,因此內核立即返回一個EWOULDBLOCK錯誤。第4次調用recvfrom時,數據報已經準備好了,被拷貝到應用緩衝區,recvfrom返回成功指示,接着就是我們處理數據報。
當一個應用進程像這樣對一個非阻塞sockt循環調用recvfrom時,我們稱此過程爲輪詢(polling).應用進程連續不斷的查詢內核,看看某操作是否準備好,這對CPU是極大的浪費,但這種模型只是偶爾才遇到。
當一個應用進程像這樣對一個非阻塞sockt循環調用recvfrom時,我們稱此過程爲輪詢(polling).應用進程連續不斷的查詢內核,看看某操作是否準備好,這對CPU是極大的浪費,但這種模型只是偶爾才遇到。
I/O複用模型
I/O複用能讓一個或多個I/O條件滿足(例如,輸入已經準備好被讀,或者描述字可以承接更多的輸出)時,我們就被通知到。I/O複用由select和poll支持,較新的Posix.1g也支持(pselect)。
I/O複用典型地用在下列網絡應用場合:
1.當客戶處理多個描述字時,必須使用。
2.一個客戶同時處理多個sockt.
3.如果一個服務器既要處理監聽sockt,又要處理連接sockt,一般也用到。
4.如果一個服務器既要處理TCP,又要處理UDP,一般也用到。
5.如果一個服務器要處理多個服務或多個協議(例如inetd守護進程),一般也用到。
I/O複用典型地用在下列網絡應用場合:
1.當客戶處理多個描述字時,必須使用。
2.一個客戶同時處理多個sockt.
3.如果一個服務器既要處理監聽sockt,又要處理連接sockt,一般也用到。
4.如果一個服務器既要處理TCP,又要處理UDP,一般也用到。
5.如果一個服務器要處理多個服務或多個協議(例如inetd守護進程),一般也用到。
I/O複用並非限於網絡編程,許多正是應用程序也需要使用這項技術。
有了I/O複用,我們就可以調用select或poll,在這兩個系統調用中的某一個上阻塞,而不阻塞於真正的I/O系統調用。圖3是I/O複用模型的一個小結。圖3 I/O複用模型
我們阻塞於select調用,等待數據報socket可讀,當select返回socket可讀條件時,我們調用recvfrom將數據報拷貝到應用緩存區中。將圖3與圖1比較,似乎沒有顯示什麼優越性,實際上因使用了select,要求2此係統調用而不是一次,好像變的還有點差,但是select的好處在於我們可以等待多個描述字準備好。
信號驅動 I/O模型
圖4 信號驅動 I/O模型
首先我們允許sockt進行信號驅動 I/O,並通過系統調用sigaction安裝一個信號處理程序。此係統調用立即返回,進程繼續工作,它是非阻塞的。當數據報準備好被讀時,就爲該進程生成個SIGIO信號。我們隨即可以在信號處理程序中調用recvfrom來讀取數據報,並通知主循環數據已準備好被處理,也可以通知主循環,讓它來處理數據報。
無論我們如何處理SIGIO信號,這種模型的好處是當等待數據報到達時,可以不阻塞。主循環可以繼續執行,只是等待信號處理程序的通知:或者數據報已準備好被處理,或者數據報已準備好被讀取。
異步I/O模型
異步I/O模型是Posix.1的1993版本中的新內容。我們讓內核啓動操作,並在整個操作完成後(包括將數據報從內核拷貝到我們自己的緩衝區)通知我們。這種模型與信號驅動模型的主要區別在於:信號驅動I/O是有內核通知我們何時可以啓動一個I/O操作,而異步I/O模型是由內核通知我們I/O操作何時完成。圖5給出了一個例子。
圖5 異步I/O模型
我們調用aio_red(Posix異步I/O函數以aio_或lio_開頭),給內核傳遞描述字、緩衝區指針、緩衝區大小(與red相同的3個參數)、文件偏移(與lseek類似),並高書內核當整個操作完成時如何通知我們。此係統調用立即返回,我們的進程不阻塞於等待I/O操作的完成。在此例子中,我們假設要求內核在操作完成時產生一個信號,此信號直到數據已拷貝到應用程序緩衝區才產生,這一點是於信號驅動I/O模型不同的。
各種I/O模型的比較圖6 各類I/O模型的比較
圖6給出了上述5中I/O模型的比較。它表明:前4種模型的區別都在第1階段,因爲前4種模型的第2階段基本相同:在數據從內核拷貝到調用者的緩衝區時,進程阻塞於recvfrom調用。然而異步I/O處理的兩個階段都不同於前4個模型。
同步I/O與異步I/O
Posix.1定義這兩個術語如下:
1.同步I/O操作引起請求進程阻塞,直到I/O操作完成。
2.異步 I/O操作不引起請求進程阻塞。
根據上述定義,我們的前四個I/O模型都是同步I/O模型,因爲真正的I/O操作(recvfrom)阻塞進程,只有異步I/O模型與異步I/O的定義相符合。