阻塞 IO 模型
在Linux ,默認(rèn)情況下所有的 socket 都是阻塞的,一個(gè)典型的讀操作流程如圖所示。
阻塞和非阻塞的概念描述的是用戶(hù)線程調(diào)用內(nèi)核 IO 操作的方式:阻塞是指 IO 操作需要徹底完成后才返回到用戶(hù)空間;而非阻塞是指 IO操作被調(diào)用后立即返回給用戶(hù)一個(gè)狀態(tài)值,不需要等到 IO 操作徹底完成。
當(dāng)應(yīng)用進(jìn)程調(diào)用了 recvfrom 這個(gè)系統(tǒng)調(diào)用后,系統(tǒng)內(nèi)核就開(kāi)始了 IO 的第一個(gè)階段 :準(zhǔn)備數(shù)據(jù)。
對(duì)于網(wǎng)絡(luò) IO 來(lái)說(shuō),很多時(shí)候數(shù)據(jù)在一開(kāi)始還沒(méi)到達(dá)時(shí),系統(tǒng)內(nèi)核就要等待足夠的數(shù)據(jù)到來(lái)。而在用戶(hù)進(jìn)程這邊,整個(gè)進(jìn)程會(huì)被阻塞。
當(dāng)系統(tǒng)內(nèi)核一直等到數(shù)據(jù)準(zhǔn)備好了,它就會(huì)將數(shù)據(jù)從系統(tǒng)內(nèi)核中拷貝到用戶(hù)內(nèi)存中,然后系統(tǒng)內(nèi)核返回結(jié)果,用戶(hù)進(jìn)程才解除阻塞的狀態(tài),重新運(yùn)行起來(lái)。所以,阻塞IO 模型的特點(diǎn)就是 IO 執(zhí)行的兩個(gè)階段都被阻塞了。
大部分的 socke接口都是阻塞型的。所謂阻塞型接口是指系統(tǒng)調(diào)用時(shí)卻不返回調(diào)用結(jié)果,并讓當(dāng)前線程一直處于阻塞狀態(tài),只有當(dāng)該系統(tǒng)調(diào)用獲得結(jié)果或者超時(shí)出錯(cuò)時(shí)才返回結(jié)果。
實(shí)際上,除非特別指定,幾乎所有的 IO 接口都阻塞型的。這給網(wǎng)絡(luò)編程帶來(lái)了一個(gè)很大的問(wèn)題,如在調(diào)用 send的同時(shí),線程處于阻塞狀態(tài),則在此期間,線程將無(wú)法執(zhí)行任何運(yùn)算或響應(yīng)任何網(wǎng)絡(luò)請(qǐng)求。
非阻塞 IO 模型
在Linux 下,可以通過(guò)設(shè)置 socket IO 變?yōu)榉亲枞麪顟B(tài)。當(dāng)一個(gè)非阻塞的 socket執(zhí)行 read 操作時(shí),流程如圖:
當(dāng)用戶(hù)進(jìn)程發(fā)出 read 操作時(shí),如果內(nèi)核中的數(shù)據(jù)還沒(méi)有準(zhǔn)備好,那么它并不會(huì) block 用戶(hù)進(jìn)程,而是立刻返回一個(gè)錯(cuò)誤。
從用戶(hù)進(jìn)程角度講,它發(fā)起 read 操作后,并不需要等待,而是馬上就得到了一個(gè)結(jié)果當(dāng)用戶(hù)進(jìn)程判斷結(jié)果是一個(gè)錯(cuò)誤時(shí),它就知道數(shù)據(jù)還沒(méi)有準(zhǔn)備好,于是它可以再次發(fā)送 read 操作。
一旦內(nèi)核中的數(shù)據(jù)準(zhǔn)備好了,并且又再次收到了用戶(hù)進(jìn)程的系統(tǒng)調(diào)用,那么它馬上就將數(shù)據(jù)復(fù)制到了用戶(hù)內(nèi)存中,然后返回正確的返回值。
所以,在非阻塞式 IO 中,用戶(hù)進(jìn)程其實(shí)需要不斷地主動(dòng)詢(xún)問(wèn) kernel數(shù)據(jù)是否準(zhǔn)備好。非阻塞的接口相比于阻塞型接口的顯著差異在于被調(diào)用之后立即返回,使用如下的函數(shù)可以將某句柄歸設(shè)為非阻塞狀態(tài):fcntl( fd , F_SETFL, O_NONBLOCK);
在非阻塞狀態(tài)下,recv 接口在被調(diào)用后立即返回,返回值代表了不同的含義,如下所述。
recv 返回值大于 0,表示接收數(shù)據(jù)完畢,返回值即是接收到的字節(jié)數(shù)。
recv 返回 0,表示連接已經(jīng)正常斷開(kāi)。
recv 返回 -1 ,且 errno 等于 EAGAIN ,表示 recv 操作還沒(méi)執(zhí)行完成。
recv 返回 -1,且 errno 不等于 EAGAIN ,表示 recv 操作遇到系統(tǒng)錯(cuò)誤 errno。
可以看到服務(wù)器線程可以通過(guò)循環(huán)調(diào)用 recv 接口,可以在單個(gè)線程內(nèi)實(shí)現(xiàn)對(duì)所有連接的數(shù)據(jù)接收。但是上述模型絕不被推薦,因?yàn)檠h(huán)調(diào)用 recv將大幅度占用 CPU 使用率。
此外,在這個(gè)方案 recv 更多的是起到檢測(cè)“操作是否完成”的作用,實(shí)際操作系統(tǒng)提供了更為高效的檢測(cè)“操作是否完成”作用的接口,例如 select多路復(fù)用模式,可以次檢測(cè)多個(gè)連接是存活躍。
-
IO
+關(guān)注
關(guān)注
0文章
448瀏覽量
39160 -
內(nèi)核
+關(guān)注
關(guān)注
3文章
1372瀏覽量
40293 -
Linux
+關(guān)注
關(guān)注
87文章
11304瀏覽量
209536 -
網(wǎng)絡(luò)
+關(guān)注
關(guān)注
14文章
7568瀏覽量
88796
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論