程序員應(yīng)該這樣理解IO
引言
很多程序員會談及IO篡九,仿佛是種時尚或者給自己貼金的方式鲁纠,因?yàn)樘嵘纤话銜婕皯?yīng)用程序的性能相關(guān)話題趴久,離高深的底層知識更接近了匾荆。但多數(shù)人的理解其實(shí)是模糊的,他們的腦海里會浮動著抽象的場景:IO就是輸入輸出啊粒督,電腦在磁盤上讀寫就是IO陪竿,僅此而已,或者屠橄,更高明一點(diǎn)族跛,他們隱隱約約的意識到IO是個耗性能的家伙。
原諒這些膚淺的程序員吧锐墙,他們在無數(shù)個夜晚挑燈夜戰(zhàn)礁哄,加班加點(diǎn),做項目趕進(jìn)度溪北,又基本沒有個人生活桐绒,還隨時可能猝死,哪里還有什么時間回爐深造之拨,更何況作者也曾經(jīng)是他們中的一員茉继。
什么是IO
IO是輸入input輸出output的首字母縮寫形式,直觀意思是計算機(jī)輸入輸出蚀乔,它描述的是計算機(jī)的數(shù)據(jù)流動的過程烁竭,因此IO第一大特征是有數(shù)據(jù)的流動;另外吉挣,對于一次IO派撕,它究竟是輸入還是輸出婉弹,是針對不同的主體而言的,不同的主體有不同的描述腥刹。例如马胧,甲乙兩人交談汉买,甲將大腦中的想法通過聲帶震動衔峰,繼而通過聲波傳入乙的耳朵,乙通過耳膜的震動再由神經(jīng)將信號解析到大腦蛙粘,這個數(shù)據(jù)流動的過程對甲而言是輸出垫卤,對乙而言是輸入。
因此出牧,理解IO一定要弄清楚所要研究的本體穴肘。
下面,我們從三個層面來理解IO舔痕。
首先评抚,從直觀層面去理解IO。
此時伯复,IO是計算機(jī)和外設(shè)之間的數(shù)據(jù)流動過程慨代,本體是一個有使用意義的可運(yùn)行的電腦,它是計算機(jī)運(yùn)行的完全必要部分啸如。姑且認(rèn)為這個完全必要部分是臺式電腦的主機(jī)侍匙,里面有CPU、內(nèi)存叮雳、主板想暗、電源等設(shè)備,因?yàn)橛辛诉@些帘不,一臺有使用意義的電腦即可運(yùn)行说莫。有了主機(jī),并不能方便的為人所服務(wù)寞焙,因此得有外設(shè)储狭。外設(shè)是電腦的外圍設(shè)備,如顯示器棺弊、鍵盤晶密、鼠標(biāo)等,它們是完成人機(jī)交互的輔助工具模她。外設(shè)包含兩種重要設(shè)備(但不限于此):輸入設(shè)備和輸出設(shè)備稻艰。像鼠標(biāo)鍵盤屬于輸入設(shè)備,將人的指令轉(zhuǎn)成“鼠鍵行為”這種數(shù)據(jù)傳給主機(jī)侈净;顯示器是輸出設(shè)備尊勿,主機(jī)通過運(yùn)算僧凤,把“返回信息”這種數(shù)據(jù)傳給顯示器。
其次元扔,從計算機(jī)架構(gòu)的角度去理解IO躯保。
從計算機(jī)架構(gòu)上來講,任何涉及到計算機(jī)核心(CPU和內(nèi)存)與其他設(shè)備間的數(shù)據(jù)轉(zhuǎn)移的過程就是IO澎语。本體就是計算機(jī)核心(CPU和內(nèi)存)途事。例如從硬盤上讀取數(shù)據(jù)到內(nèi)存,是一次輸入擅羞,將內(nèi)存中的數(shù)據(jù)寫入到硬盤就產(chǎn)生了輸出尸变。在計算機(jī)的世界里,這就是IO的本質(zhì)减俏。
最后召烂,從編程的角度去理解IO。
此時娃承,IO的主體是其應(yīng)用程序的運(yùn)行態(tài)奏夫,即進(jìn)程,特別強(qiáng)調(diào)的是我們的應(yīng)用程序其實(shí)并不存在實(shí)質(zhì)的IO過程历筝,真正的IO過程是操作系統(tǒng)的事情酗昼,這里把應(yīng)用程序的IO操作分為兩種動作:IO調(diào)用和IO執(zhí)行。IO調(diào)用是由進(jìn)程發(fā)起漫谷,IO執(zhí)行是操作系統(tǒng)的工作仔雷。因此,更準(zhǔn)確些來說舔示,此時所說的IO是應(yīng)用程序?qū)Σ僮飨到y(tǒng)IO功能的一次觸發(fā)碟婆,即IO調(diào)用。
IO調(diào)用的目的是將進(jìn)程的內(nèi)部數(shù)據(jù)遷移到外部即輸出惕稻,或?qū)⑼獠繑?shù)據(jù)遷移到進(jìn)程內(nèi)部即輸入竖共。這里,外部數(shù)據(jù)指非進(jìn)程空間數(shù)據(jù)俺祠,在編程時公给,通常討論的場景是來自外部存儲設(shè)備的數(shù)據(jù),如硬盤蜘渣、CD-ROM淌铐、以及需要socket通信傳輸?shù)木W(wǎng)絡(luò)數(shù)據(jù)。
以一個進(jìn)程的輸入類型的IO調(diào)用為例蔫缸,它將完成或引起如下工作內(nèi)容:
- 進(jìn)程向操作系統(tǒng)請求外部數(shù)據(jù)
- 操作系統(tǒng)將外部數(shù)據(jù)加載到內(nèi)核緩沖區(qū)
- 操作系統(tǒng)將數(shù)據(jù)從內(nèi)核緩沖區(qū)拷貝到進(jìn)程緩沖區(qū)
- 進(jìn)程讀取數(shù)據(jù)繼續(xù)后面的工作
從上面的描述來看腿准,我們更容易理解一個IO操作,應(yīng)用程序和操作系統(tǒng)都干了些什么拾碌,也幫助我們更容器理解阻塞和非阻塞吐葱,異步和同步的相關(guān)IO編程概念街望。
阻塞和非阻塞IO
阻塞和非阻塞強(qiáng)調(diào)的是進(jìn)程對于操作系統(tǒng)IO是否處于就緒狀態(tài)的處理方式。
上面已經(jīng)說過弟跑,應(yīng)用程序的IO實(shí)際是分為兩個步驟灾前,IO調(diào)用和IO執(zhí)行。IO調(diào)用是由進(jìn)程發(fā)起孟辑,IO執(zhí)行是操作系統(tǒng)的工作哎甲。操作系統(tǒng)的IO情況決定了進(jìn)程IO調(diào)用是否能夠得到立即響應(yīng)。如進(jìn)程發(fā)起了讀取數(shù)據(jù)的IO調(diào)用扑浸,操作系統(tǒng)需要將外部數(shù)據(jù)拷貝到進(jìn)程緩沖區(qū)烧给,在有數(shù)據(jù)拷貝到進(jìn)程緩沖區(qū)前,進(jìn)程緩沖區(qū)處于不可讀狀態(tài)喝噪,我們稱之為操作系統(tǒng)IO未就緒。
進(jìn)程的IO調(diào)用是否能得到立即執(zhí)行是需要操作系統(tǒng)IO處于就緒狀態(tài)的指么,對于讀取數(shù)據(jù)的操作酝惧,如果操作系統(tǒng)IO處于未就緒狀態(tài),當(dāng)前進(jìn)程或線程如果一直等待直到其就緒伯诬,該種IO方式為阻塞IO晚唇。如果進(jìn)程或線程并不一直等待其就緒,而是可以做其他事情盗似,這種方式為非阻塞IO哩陕。所以對于非阻塞IO,我們編程時需要經(jīng)常去輪詢就緒狀態(tài)赫舒。
異步和同步IO
我們經(jīng)常會談及同步IO和異步IO悍及。同步和異步描述的是針對當(dāng)前執(zhí)行線程、或進(jìn)程而言接癌,發(fā)起IO調(diào)用后心赶,當(dāng)前線程或進(jìn)程是否掛起等待操作系統(tǒng)的IO執(zhí)行完成。
我們說一個IO執(zhí)行是同步執(zhí)行的缺猛,意思是程序發(fā)起IO調(diào)用缨叫,當(dāng)前線程或進(jìn)程需要等待操作系統(tǒng)完成IO工作并告知進(jìn)程已經(jīng)完成,線程或進(jìn)程才能繼續(xù)往下執(zhí)行其他既定指令荔燎。
如果說一個IO執(zhí)行是異步的耻姥,意思是該動作是由當(dāng)前線程或進(jìn)程請求發(fā)起,且當(dāng)前線程或進(jìn)程不必等待操作系統(tǒng)IO的執(zhí)行完畢有咨,可直接繼續(xù)往下執(zhí)行其他既定指令琐簇。操作系統(tǒng)完成IO后,當(dāng)前線程或進(jìn)程會得到操作系統(tǒng)的通知摔吏。
以一個讀取數(shù)據(jù)的IO操作而言鸽嫂,在操作系統(tǒng)將外部數(shù)據(jù)寫入進(jìn)程緩沖區(qū)這個期間纵装,進(jìn)程或線程掛起等待操作系統(tǒng)IO執(zhí)行完成的話,這種IO執(zhí)行策略就為同步据某,如果進(jìn)程或線程并不掛起而是繼續(xù)工作橡娄,這種IO執(zhí)行策略便為異步。
總結(jié)
綜上癣籽,從編程的角度講述了程序員應(yīng)該如何去理解IO編程的一些概念挽唉。其關(guān)鍵點(diǎn)是要將應(yīng)用程序的IO操作分為兩個步驟來理解:IO調(diào)用和IO執(zhí)行。IO調(diào)用才是應(yīng)用程序干的事情筷狼,而IO執(zhí)行是操作系統(tǒng)的工作瓶籽。在IO調(diào)用時,對待操作系統(tǒng)IO就緒狀態(tài)的不同方式埂材,決定了其是阻塞或非阻塞模式塑顺;在IO執(zhí)行時,線程或進(jìn)程是否掛起等待IO執(zhí)行決定了其是否為同步或異步IO俏险。