Linux中一切皆文件,不論是我們存儲(chǔ)在磁盤上的字符文件,可執(zhí)行文件還是我們的接入電腦的I/O設(shè)備等都被VFS抽象成了文件,比如標(biāo)準(zhǔn)輸入設(shè)備默認(rèn)是鍵盤,我們?cè)诓僮鳂?biāo)準(zhǔn)輸入設(shè)備的時(shí)候,其實(shí)操作的是默認(rèn)打開(kāi)的一個(gè)文件描述符是0的文件,而一切軟件操作硬件都需要通過(guò)OS,而OS操作一切硬件都需要相應(yīng)的驅(qū)動(dòng)程序,這個(gè)驅(qū)動(dòng)程序里配置了這個(gè)硬件的相應(yīng)配置和使用方法。Linux的I/O分為阻塞I/O,非阻塞I/O,I/O多路復(fù)用,信號(hào)驅(qū)動(dòng)I/O四種。對(duì)于I/O設(shè)備的驅(qū)動(dòng),一般都會(huì)提供關(guān)于阻塞和非阻塞兩種配置。我們最常見(jiàn)的I/O設(shè)備之一--鍵盤(標(biāo)準(zhǔn)輸入設(shè)備)的驅(qū)動(dòng)程序默認(rèn)是阻塞的。
多路復(fù)用就是為了使進(jìn)程能夠從多個(gè)阻塞I/O中獲得自己想要的數(shù)據(jù)并繼續(xù)執(zhí)行接下來(lái)的任務(wù)。其主要的思路就是同時(shí)監(jiān)視多個(gè)文件描述符,如果有文件描述符的設(shè)定狀態(tài)的被觸發(fā),就繼續(xù)執(zhí)行進(jìn)程,如果沒(méi)有任何一個(gè)文件描述符的設(shè)定狀態(tài)被觸發(fā),進(jìn)程進(jìn)入sleep
多路復(fù)用的一個(gè)主要用途就是實(shí)現(xiàn)"I/O多路復(fù)用并發(fā)服務(wù)器",和多線程并發(fā)或者多進(jìn)程并發(fā)相比,這種服務(wù)器的系統(tǒng)開(kāi)銷更低,更適合做web服務(wù)器,但是由于其并沒(méi)有實(shí)現(xiàn)真正的多任務(wù),所以當(dāng)壓力大的時(shí)候,部分用戶的請(qǐng)求響應(yīng)會(huì)較慢
阻塞I/O
阻塞I/O,就是當(dāng)進(jìn)程試圖訪問(wèn)這個(gè)I/O設(shè)備而這個(gè)設(shè)備并沒(méi)有準(zhǔn)備好的時(shí)候,設(shè)備的驅(qū)動(dòng)程序會(huì)通過(guò)內(nèi)核讓這個(gè)試圖訪問(wèn)的進(jìn)程進(jìn)入sleep狀態(tài)。阻塞I/O的一個(gè)好處就是可以大大的節(jié)約CPU時(shí)間,因?yàn)橐坏┮粋€(gè)進(jìn)程試圖訪問(wèn)一個(gè)沒(méi)有準(zhǔn)備好的阻塞I/O,就會(huì)進(jìn)入sleep狀態(tài),而進(jìn)入sleep狀態(tài)的進(jìn)程是不在內(nèi)核的進(jìn)程調(diào)度鏈表中,直到目標(biāo)I/O準(zhǔn)備好了將其喚醒并加入調(diào)度鏈表,這樣就可以節(jié)約CPU時(shí)間。當(dāng)然阻塞I/O也有其固有的缺點(diǎn),如果進(jìn)程試圖訪問(wèn)一個(gè)阻塞I/O,但是否訪問(wèn)成功并不對(duì)接下來(lái)的任務(wù)有決定性影響,那么直接使其進(jìn)入sleep狀態(tài)顯然會(huì)延誤其任務(wù)的完成。
-
典型的默認(rèn)阻塞IO有標(biāo)準(zhǔn)輸入設(shè)備,socket設(shè)備,管道設(shè)備等,當(dāng)我們使用gets(),scanf(),read()等操作請(qǐng)求這些IO時(shí)而IO并沒(méi)有數(shù)據(jù)流入,就會(huì)造成進(jìn)程的sleep。 進(jìn)程會(huì)一直阻塞下去直到接收緩沖區(qū)中有數(shù)據(jù)可讀,此時(shí)內(nèi)核再去喚醒該進(jìn)程,通過(guò)相應(yīng)的函數(shù)從中獲取數(shù)據(jù)。如果阻塞過(guò)程中對(duì)方發(fā)生故障,那么這個(gè)進(jìn)程將會(huì)永遠(yuǎn)阻塞下去。
- 寫操作時(shí)發(fā)生阻塞的情況要比讀操作少,主要發(fā)生在要寫入的緩沖區(qū)的大小小于要寫入的數(shù)據(jù)量的情況下,這時(shí)寫操作將不進(jìn)行任何任何拷貝工作,將發(fā)生阻塞。一旦發(fā)送緩沖區(qū)內(nèi)有足夠的空間,內(nèi)核將喚醒進(jìn)程,將數(shù)據(jù)從用戶緩沖區(qū)中拷貝到相應(yīng)的發(fā)送數(shù)據(jù)緩沖區(qū)。udp不用等待確認(rèn),沒(méi)有實(shí)際的發(fā)送緩沖區(qū),所以u(píng)dp協(xié)議中不存在發(fā)送緩沖區(qū)滿的情況,在udp套接字上執(zhí)行的寫操作永遠(yuǎn)都不會(huì)阻塞
現(xiàn)假設(shè)一個(gè)進(jìn)程希望通過(guò)三個(gè)管道中任意一個(gè)中讀取數(shù)據(jù)并顯示,偽代碼如下
read(pipe_0,buf,sizeof(buf)); //sleep print buf; read(pipe_1,buf,sizeof(buf)); print buf; read(pipe_2,buf,sizeof(buf)); print buf;
由于管道是阻塞I/O,所以如果pipe