一、相關(guān)概念
- 流
可以進(jìn)行I/O操作的內(nèi)核對(duì)象,如文件、套接字socket、管道pipe
二、同步異步、阻塞非阻塞
同步
發(fā)出一個(gè)同步功能調(diào)用時(shí),在得到結(jié)果之前該調(diào)用就不返回。也就是必須一件一件事做,等前一件做完了才能做下一件事。
異步
發(fā)出一個(gè)異步功能調(diào)用時(shí),調(diào)用者不能立刻得到結(jié)果,但仍然返回調(diào)用并繼續(xù)執(zhí)行其他功能。實(shí)際處理這個(gè)調(diào)用的部件在完成后,通過狀態(tài)、通知和回調(diào)來通知調(diào)用者。
阻塞
阻塞調(diào)用的結(jié)果返回之前,當(dāng)前線程會(huì)被掛起(線程進(jìn)入非可執(zhí)行狀態(tài),在這個(gè)狀態(tài)下,cpu不會(huì)給線程分配時(shí)間片,即線程暫停運(yùn)行)。函數(shù)只有在得到結(jié)果之后才會(huì)返回。
- 阻塞與同步的區(qū)別:當(dāng)前線程是否仍然處于激活狀態(tài)
【同步】到A樓(內(nèi)核緩沖區(qū))取快遞,但是不知道快遞什么時(shí)候過來,但又不能干別的事,只能死等著。
【阻塞】但你可以在A樓睡覺(進(jìn)程處于休眠狀態(tài)),因?yàn)槟阒揽爝f把貨送來時(shí)一定會(huì)給你打個(gè)電話叫醒你。
非阻塞
非阻塞調(diào)用的結(jié)果返回之前,當(dāng)前線程會(huì)立刻返回,不會(huì)阻塞當(dāng)前線程。
- 同步和異步的區(qū)別
數(shù)據(jù)訪問的時(shí)候進(jìn)程是否阻塞。指當(dāng)server端的進(jìn)程需要訪問的數(shù)據(jù)如果尚未就緒,進(jìn)程是否需要等待。 - 阻塞和非阻塞的區(qū)別
應(yīng)用程序的調(diào)用是否立即返回。是client端訪問數(shù)據(jù)的機(jī)制。
三、linux下的五種IO模型

前四種屬于同步,第五種為異步。
1、阻塞I/O(blocking I/O)
應(yīng)用程序調(diào)用一個(gè)IO函數(shù),導(dǎo)致應(yīng)用程序阻塞,等待數(shù)據(jù)準(zhǔn)備好。
在socket編程中,當(dāng)調(diào)用recv()函數(shù)時(shí),系統(tǒng)首先查是否有準(zhǔn)備好的數(shù)據(jù)。如果數(shù)據(jù)沒有準(zhǔn)備好,那么系統(tǒng)就處于等待狀態(tài)。當(dāng)數(shù)據(jù)準(zhǔn)備好后,將數(shù)據(jù)從系統(tǒng)緩沖區(qū)復(fù)制到用戶空間,然后該函數(shù)返回。
2、非阻塞I/O (nonblocking I/O)
把一個(gè)SOCKET接口設(shè)置為非阻塞就是告訴內(nèi)核,當(dāng)所請(qǐng)求的I/O操作無(wú)法完成時(shí),不要將進(jìn)程睡眠,而是返回一個(gè)錯(cuò)誤。這樣I/O操作函數(shù)將不斷的測(cè)試數(shù)據(jù)是否已經(jīng)準(zhǔn)備好,如果沒有準(zhǔn)備好,繼續(xù)測(cè)試,直到數(shù)據(jù)準(zhǔn)備好為止。在這個(gè)不斷測(cè)試的過程中,會(huì)大量的占用CPU的時(shí)間。
在調(diào)用recv()時(shí),如果沒有數(shù)據(jù)也不要讓線程睡眠,而應(yīng)該立即返回,該函數(shù)返回一個(gè)錯(cuò)誤代碼。然后繼續(xù)調(diào)用recv()函數(shù),繼續(xù)返回錯(cuò)誤碼……直到緩沖區(qū)中有數(shù)據(jù),recv返回正確碼。
3、 I/O復(fù)用(select 和poll) (I/O multiplexing)
關(guān)鍵是能實(shí)現(xiàn)同時(shí)對(duì)多個(gè)IO端口進(jìn)行監(jiān)聽。
主要使用了select、poll、epoll三個(gè)函數(shù),它們會(huì)使進(jìn)程阻塞,但是和阻塞I/O所不同的是,可以同時(shí)阻塞多個(gè)I/O操作,而且可以同時(shí)對(duì)多個(gè)讀操作、多個(gè)寫操作的I/O函數(shù)進(jìn)行檢測(cè),直到有數(shù)據(jù)可讀或可寫時(shí),才真正調(diào)用I/O操作函數(shù)。
4、信號(hào)驅(qū)動(dòng)I/O (signal driven I/O (SIGIO))
首先允許套接口進(jìn)行信號(hào)驅(qū)動(dòng)I/O,并安裝一個(gè)信號(hào)處理函數(shù),進(jìn)程繼續(xù)運(yùn)行并不阻塞。當(dāng)數(shù)據(jù)準(zhǔn)備好時(shí),進(jìn)程會(huì)收到一個(gè)SIGIO信號(hào),可以在信號(hào)處理函數(shù)中調(diào)用I/O操作函數(shù)處理數(shù)據(jù)。
5、異步I/O (asynchronous I/O)
當(dāng)一個(gè)異步過程調(diào)用發(fā)出后,調(diào)用者不能立刻得到結(jié)果。實(shí)際處理這個(gè)調(diào)用的部件在完成后,通過狀態(tài)、通知和回調(diào)來通知調(diào)用者的輸入輸出操作
四、IO復(fù)用中的select、poll、epoll
select,poll,epoll都是IO多路復(fù)用的機(jī)制。
I/O多路復(fù)用就通過一種機(jī)制,可以監(jiān)視多個(gè)描述符,一旦某個(gè)描述符就緒(一般是讀就緒或者寫就緒),能夠通知程序進(jìn)行相應(yīng)的讀寫操作。但select,poll,epoll本質(zhì)上都是同步I/O,因?yàn)樗麄兌夹枰谧x寫事件就緒后自己負(fù)責(zé)進(jìn)行讀寫,也就是說這個(gè)讀寫過程是阻塞的,而異步I/O則無(wú)需自己負(fù)責(zé)進(jìn)行讀寫,異步I/O的實(shí)現(xiàn)會(huì)負(fù)責(zé)把數(shù)據(jù)從內(nèi)核拷貝到用戶空間。
select:O(n)
無(wú)差別輪詢所有流,找出能讀出數(shù)據(jù),或者寫入數(shù)據(jù)的流,對(duì)他們進(jìn)行操作。
單個(gè)進(jìn)程可監(jiān)視的端口fd數(shù)量被限制,具體數(shù)目可以cat /proc/sys/fs/file-max察看。32位機(jī)默認(rèn)是1024個(gè)。64位機(jī)默認(rèn)是2048。
poll:O(n)
本質(zhì)操作與select無(wú)差別,區(qū)別是poll基于鏈表來存儲(chǔ)的,無(wú)最大連接數(shù)的限制。
epoll:O(1)
事件驅(qū)動(dòng)的event poll,不需要無(wú)差別輪詢,epoll會(huì)把哪個(gè)流發(fā)生了怎樣的I/O事件通知我們。
epoll有EPOLLLT和EPOLLET兩種觸發(fā)模式,LT是默認(rèn)模式,ET是高速模式。
- LT模式(水平觸發(fā))
只要這個(gè)fd還有數(shù)據(jù)可讀,每次epoll_wait都會(huì)返回它的事件,提醒用戶程序去操作 -
ET模式(邊緣觸發(fā))
它只會(huì)提示一次,下次再有數(shù)據(jù)流入前都不會(huì)再提示了,無(wú)論fd中是否還有數(shù)據(jù)可讀。所以在ET模式下,read一個(gè)fd的時(shí)候一定要把它的buffer讀光,也就是說一直讀到read的返回值小于請(qǐng)求值,或者遇到EAGAIN錯(cuò)誤。
LT和ET的區(qū)別
