硬件上下文切換
每個進程都擁有自己的內(nèi)存空間,CPU任何時間只能運行一個進程. 運行之前,每個進程需要將內(nèi)存狀態(tài)復(fù)制到CPU的寄存器才能工作。這種復(fù)制就是進程的上下文切換
二種切換都會導(dǎo)致成本升高
1.進程內(nèi)存-CPU寄存器相互切換 硬件上下文 解決:上下文切換一般都是由阻塞引起的,所以使用非阻塞
2.進程二種運行狀態(tài)的切換 用戶態(tài)-內(nèi)核態(tài) 解決:減少不避要的系統(tǒng)調(diào)用。
阻塞的特點:進程睡眠
阻塞: 如果條件未就緒,'你'必須死等它就緒;進程睡眠(睡眠的缺點就是會讓出CPU控制權(quán))
非阻塞:如果條件未就緒,'你'可以轉(zhuǎn)身作別的事情;進程可以作任何想做的事情,不過通常是低效的輪詢。(輪詢的特點是CPU寄存器一直被當(dāng)前進程使用。輪詢不是一種好的方式,可以使用wait/notify機制)
以這種理解方式,阻塞/非阻塞只對同步操作有意義;異步I/O總是意味著進程不會因為I/O陷入睡眠。
阻塞非阻塞示例:
比如一個人去銀行辦業(yè)務(wù),拿到流水號之后,一邊抬頭看顯示屏幕上當(dāng)前顯示的流水號一邊打電話這就是同步非阻塞。(當(dāng)前進程在等待的過程中可以做其它事情,注意,如果將柜臺觸發(fā)流水號理解為消息通知的話,就能避免用戶不停的在二件事情上來回切換,此時用戶只需要打你的電話就OK了,不用再抬頭看流水號是否輪到了自己,這件事情交給了銀行柜臺去做,因為柜臺與用戶是二個主體各做一件事情,這不會象用戶一個人同時做二件事情而引起低效輪詢)
一個廣義概念的read/write應(yīng)該包含的時間:
數(shù)據(jù)就緒的時間+數(shù)據(jù)讀寫的時間
注:進程等待數(shù)據(jù)就緒,可以阻塞(傻等),也可以非阻塞(同時做其它事情)。進程進行數(shù)據(jù)讀寫可以使用阻塞或非阻塞的方法。
select是同步還是異步:
經(jīng)??吹揭恍┘夹g(shù)博客或書籍講IO的多路就緒通知中的select方式是異步阻塞。
1.如果按照同步與異步只取決于消息通知,不包括處理消息的話,select是異步的方式。示例:只要柜臺通知了我就算異步,至于辦理什么業(yè)務(wù)即如何處理消息不參與到同步異步的劃分。此時select是異步阻塞方式。
2.如果按照UNP的劃分的話,同步/異步不僅僅包括消息通知,還包括了對消息的處理。select并沒有什么神秘的地方,它只是監(jiān)控了多個fd的狀態(tài)而不是一個。select實際上完成的只是read/write的第一部分:等待條件就緒;唯一的改進是可以等待多個條件。"select + read/write"的調(diào)用形式容易產(chǎn)生"系統(tǒng)通知我條件就緒"的假象,可實際上你不過是在條件就緒的時候醒來,然后仍然親自動手完成了數(shù)據(jù)復(fù)制的操作。 select通知進程之后,進程還要切到內(nèi)核態(tài)調(diào)用一些系統(tǒng)函數(shù)來讀寫數(shù)據(jù),比如NIO中的Channel在獲得相應(yīng)的Channel注冊的事件已經(jīng)觸發(fā)之后,還要負(fù)責(zé)讀寫數(shù)據(jù)。所以,如果基于UNP的劃分,NIO是屬于同步非阻塞的方式,read/write、read + NON_BLOCK、select、signal driven I/O 都屬于同步I/O; 它們的共同特點是:將數(shù)據(jù)從內(nèi)核空間復(fù)制到到用戶空間的這個操作,是由用戶空間的代碼顯式發(fā)起的。
select示例:
柜臺R:只能取款
柜臺W:只能存款
read: 親自在柜臺R排隊(進程睡眠) + 取款
write: 親自在柜臺W排隊(進程睡眠) + 存款
select + read/write : 親自同時在R、W兩個柜臺排隊(進程睡眠) + (存款|取款|存款+取款)
注:我覺得二種見解都正確,角度不同而已。
什么是異步:
AIO : 告訴心腹小弟要取款若干,然后忙別的事情;小弟取款完畢將其如數(shù)奉上。只有AIO屬于異步I/O;內(nèi)核不露聲色的將數(shù)據(jù)從內(nèi)核空間復(fù)制到用戶空間,然后通知進程。(NIO在就緒之后,進程還要切換到內(nèi)核態(tài)調(diào)用系統(tǒng)函數(shù)將數(shù)據(jù)讀寫到用戶態(tài)的內(nèi)存空間。AIO直接告訴進程你可以操作用戶態(tài)空間里的數(shù)據(jù)了。)
異步示例:交代要做的事情,然后忙其他的事情;'別人'(OS)會充當(dāng)你的跑腿,在全部做完你交待的事情,然后通知你(callback);比如你交待取錢,可以做其它事情,他最終取完錢會把錢交到你的手上。
參考博客:http://www.cppblog.com/converse/archive/2009/05/13/82879.html
-------------------------------------------------------------------------------
之前的想法:
讀寫方法可以分為阻塞與非阻塞二類。NIO中Channel的讀寫方法是非阻塞的,Channel 是一個全新的原始 I/O 抽象。Channel用來表示以前的InputStream,OutputStream表示的數(shù)據(jù)的源與目的。Channel通過Buffer中轉(zhuǎn)數(shù)據(jù)。而傳統(tǒng)的IO類的讀寫操作默認(rèn)是阻塞方式的。
阻塞的讀方法中,讀阻塞與讀方法有關(guān),讀方法的參數(shù)決定了要讀多少數(shù)據(jù),如果沒有讀滿, 就會一直阻塞. 直到讀滿返回。
寫阻塞類似,具體也取決于寫方法的參數(shù)。
對于阻塞而引起的等待的問題,解決方法有二種,一種是通過多執(zhí)行流來處理,比如多線程,多進程(優(yōu)點:多線程、多進程可以有效的利用CPU資源。缺點:代價就是多進程的大量內(nèi)存開銷,多線程的上下文切換及創(chuàng)建線程本身的開銷). 另一種解決辦法JDK中的NIO,減少線程上下文的切換。在一個線程里處理多個事情,但是會導(dǎo)致CPU忙于應(yīng)付輪詢,這是低效的。
聯(lián)系客服