五種I/O 模式:
【1】 阻塞 I/O (Linux下的I/O操作默認是阻塞I/O,即open和socket創(chuàng)建的I/O都是阻塞I/O)
【2】 非阻塞 I/O (可以通過fcntl或者open時使用O_NONBLOCK參數(shù),將fd設置為非阻塞的I/O)
【3】 I/O 多路復用 (I/O多路復用,通常需要非阻塞I/O配合使用)
【4】 信號驅(qū)動 I/O (SIGIO)
【5】 異步 I/O
一般來說,程序進行輸入操作有兩步:
1.等待有數(shù)據(jù)可以讀
2.將數(shù)據(jù)從系統(tǒng)內(nèi)核中拷貝到程序的數(shù)據(jù)區(qū)。
對于sock編程來說:
第一步: 一般來說是等待數(shù)據(jù)從網(wǎng)絡上傳到本地。當數(shù)據(jù)包到達的時候,數(shù)據(jù)將會從網(wǎng)絡層拷貝到內(nèi)核的緩存中;
第二步: 是從內(nèi)核中把數(shù)據(jù)拷貝到程序的數(shù)據(jù)區(qū)中。
阻塞I/O模式 //進程處于阻塞模式時,讓出CPU,進入休眠狀態(tài)
阻塞 I/O 模式是最普遍使用的 I/O 模式。是Linux系統(tǒng)下缺省的IO模式。
大部分程序使用的都是阻塞模式的 I/O 。
一個套接字建立后所處于的模式就是阻塞 I/O 模式。(因為Linux系統(tǒng)默認的IO模式是阻塞模式)
對于一個 UDP 套接字來說,數(shù)據(jù)就緒的標志比較簡單:
(1)已經(jīng)收到了一整個數(shù)據(jù)報
(2)沒有收到。
而 TCP 這個概念就比較復雜,需要附加一些其他的變量。
一個進程調(diào)用 recvfrom ,然后系統(tǒng)調(diào)用并不返回知道有數(shù)據(jù)報到達本地系統(tǒng),然后系統(tǒng)將數(shù)據(jù)拷貝到進程的緩存中。 (如果系統(tǒng)調(diào)用收到一個中斷信號,則它的調(diào)用會被中斷)
我們稱這個進程在調(diào)用recvfrom一直到從recvfrom返回這段時間是阻塞的。當recvfrom正常返回時,我們的進程繼續(xù)它的操作。
---------------------------------------------------------------------------------
非阻塞模式I/O //非阻塞模式的使用并不普遍,因為非阻塞模式會浪費大量的CPU資源。
當我們將一個套接字設置為非阻塞模式,我們相當于告訴了系統(tǒng)內(nèi)核: “當我請求的I/O 操作不能夠馬上完成,你想讓我的進程進行休眠等待的時候,不要這么做,請馬上返回一個錯誤給我。”
我們開始對 recvfrom 的三次調(diào)用,因為系統(tǒng)還沒有接收到網(wǎng)絡數(shù)據(jù),所以內(nèi)核馬上返回一個 EWOULDBLOCK的錯誤。
第四次我們調(diào)用 recvfrom 函數(shù),一個數(shù)據(jù)報已經(jīng)到達了,內(nèi)核將它拷貝到我們的應用程序的緩沖區(qū)中,然后 recvfrom 正常返回,我們就可以對接收到的數(shù)據(jù)進行處理了。
當一個應用程序使用了非阻塞模式的套接字,它需要使用一個循環(huán)來不聽的測試是否一個文件描述符有數(shù)據(jù)可讀(稱做 polling(輪詢))。應用程序不停的 polling 內(nèi)核來檢查是否 I/O操作已經(jīng)就緒。這將是一個極浪費 CPU資源的操作。這種模式使用中不是很普遍。

例如:
對管道的操作,最好使用非阻塞方式!
---------------------------------------------------------------------------------
I/O多路復用 //針對批量IP操作時,使用I/O多路復用,非常有好。
在使用 I/O 多路技術(shù)的時候,我們調(diào)用 select()函數(shù)和 poll()函數(shù)或epoll函數(shù)(2.6內(nèi)核開始支持),在調(diào)用它們的時候阻塞,而不是我們來調(diào)用 recvfrom(或recv)的時候阻塞。
當我們調(diào)用 select函數(shù)阻塞的時候,select 函數(shù)等待數(shù)據(jù)報套接字進入讀就緒狀態(tài)。當select函數(shù)返回的時候, 也就是套接字可以讀取數(shù)據(jù)的時候。 這時候我們就可以調(diào)用 recvfrom函數(shù)來將數(shù)據(jù)拷貝到我們的程序緩沖區(qū)中。
對于單個I/O操作,和阻塞模式相比較,select()和poll()或epoll并沒有什么高級的地方。
而且,在阻塞模式下只需要調(diào)用一個函數(shù):
讀取或發(fā)送函數(shù)。
在使用了多路復用技術(shù)后,我們需要調(diào)用兩個函數(shù)了:
先調(diào)用 select()函數(shù)或poll()函數(shù),然后才能進行真正的讀寫。
多路復用的高級之處在于::
它能同時等待多個文件描述符,而這些文件描述符(套接字描述符)其中的任意一個進入讀就緒狀態(tài),select()函數(shù)就可以返回。

IO 多路技術(shù)一般在下面這些情況中被使用:
1、當一個客戶端需要同時處理多個文件描述符的輸入輸出操作的時候(一般來說是標準的輸入輸出和網(wǎng)絡套接字),I/O 多路復用技術(shù)將會有機會得到使用。
2、當程序需要同時進行多個套接字的操作的時候。
3、如果一個 TCP 服務器程序同時處理正在偵聽網(wǎng)絡連接的套接字和已經(jīng)連接好的套接字。
4、如果一個服務器程序同時使用 TCP 和 UDP 協(xié)議。
5、如果一個服務器同時使用多種服務并且每種服務可能使用不同的協(xié)議(比如 inetd就是這樣的)。
異步IO模式有::
1、信號驅(qū)動I/O模式
2、異步I/O模式
信號驅(qū)動I/O模式 //自己沒有用過。
我們可以使用信號,讓內(nèi)核在文件描述符就緒的時候使用 SIGIO 信號來通知我們。我們將這種模式稱為信號驅(qū)動 I/O 模式。

為了在一個套接字上使用信號驅(qū)動 I/O 操作,下面這三步是所必須的。
(1)一個和 SIGIO信號的處理函數(shù)必須設定。
(2)套接字的擁有者必須被設定。一般來說是使用 fcntl 函數(shù)的 F_SETOWN 參數(shù)來
進行設定擁有者。
(3)套接字必須被允許使用異步 I/O。一般是通過調(diào)用 fcntl 函數(shù)的 F_SETFL 命令,O_ASYNC為參數(shù)來實現(xiàn)。
雖然設定套接字為異步 I/O 非常簡單,但是使用起來困難的部分是怎樣在程序中斷定產(chǎn)生 SIGIO信號發(fā)送給套接字屬主的時候,程序處在什么狀態(tài)。
1.UDP 套接字的 SIGIO 信號 (比較簡單)
在 UDP 協(xié)議上使用異步 I/O 非常簡單.這個信號將會在這個時候產(chǎn)生:
1、套接字收到了一個數(shù)據(jù)報的數(shù)據(jù)包。
2、套接字發(fā)生了異步錯誤。
當我們在使用 UDP 套接字異步 I/O 的時候,我們使用 recvfrom()函數(shù)來讀取數(shù)據(jù)報數(shù)據(jù)或是異步 I/O 錯誤信息。
2.TCP 套接字的 SIGIO 信號 (不會使用)
不幸的是,異步 I/O 幾乎對 TCP 套接字而言沒有什么作用。因為對于一個 TCP 套接字來說,SIGIO 信號發(fā)生的幾率太高了,所以 SIGIO 信號并不能告訴我們究竟發(fā)生了什么事情。
在 TCP 連接中, SIGIO 信號將會在這個時候產(chǎn)生:
l 在一個監(jiān)聽某個端口的套接字上成功的建立了一個新連接。
l 一個斷線的請求被成功的初始化。
l 一個斷線的請求成功的結(jié)束。
l 套接字的某一個通道(發(fā)送通道或是接收通道)被關(guān)閉。
l 套接字接收到新數(shù)據(jù)。
l 套接字將數(shù)據(jù)發(fā)送出去。
l 發(fā)生了一個異步 I/O 的錯誤。
一個對信號驅(qū)動 I/O 比較實用的方面是 NTP(網(wǎng)絡時間協(xié)議 Network Time Protocol)服務器,它使用 UDP。這個服務器的主循環(huán)用來接收從客戶端發(fā)送過來的數(shù)據(jù)報數(shù)據(jù)包,然后再發(fā)送請求。對于這個服務器來說,記錄下收到每一個數(shù)據(jù)包的具體時間是很重要的。
因為那將是返回給客戶端的值,客戶端要使用這個數(shù)據(jù)來計算數(shù)據(jù)報在網(wǎng)絡上來回所花費的時間。圖 6-8 表示了怎樣建立這樣的一個 UDP 服務器。
異步I/O模式 //比如寫操作,只需用寫,不一定寫入磁盤(這就是異步I/O)的好處。異步IO的好處效率高。
當我們運行在異步 I/O 模式下時,我們?nèi)绻脒M行 I/O 操作,只需要告訴內(nèi)核我們要進行 I/O 操作,然后內(nèi)核會馬上返回。具體的 I/O 和數(shù)據(jù)的拷貝全部由內(nèi)核來完成,我們的程序可以繼續(xù)向下執(zhí)行。當內(nèi)核完成所有的 I/O 操作和數(shù)據(jù)拷貝后,內(nèi)核將通知我們的程序。
異步 I/O 和 信號驅(qū)動I/O的區(qū)別是:
1、信號驅(qū)動 I/O 模式下,內(nèi)核在操作可以被操作的時候通知給我們的應用程序發(fā)送SIGIO 消息。
2、異步 I/O 模式下,內(nèi)核在所有的操作都已經(jīng)被內(nèi)核操作結(jié)束之后才會通知我們的應用程序。

轉(zhuǎn)自:http://blog.163.com/xychenbaihu@yeah/blog/static/13222965520112163171778/