I/O 多路復(fù)用底層原理前篇 - 五種IO模型
前言
- BIO 、NIO 、AIO 總結(jié)
上篇講 BIO、NIO、AIO 的基本概念以及一些常見(jiàn)問(wèn)題,介紹了 NIO 是同步非阻塞 ,服務(wù)器實(shí)現(xiàn)模式為一個(gè)線程可以處理多個(gè)請(qǐng)求(連接),客戶端發(fā)送的連接請(qǐng)求都會(huì)注冊(cè)到多路復(fù)用器selector上,多路復(fù)用器輪詢到連接有IO請(qǐng)求就進(jìn)行處理。那么I/O多路復(fù)用器到底是如何實(shí)現(xiàn)的?本篇我們來(lái)一探究竟。
為了加深對(duì) I/O多路復(fù)用機(jī)制 的理解,以及了解到多路復(fù)用也有局限性,本著打破砂鍋問(wèn)到底的精神,在這里我們先回顧下 Unix網(wǎng)絡(luò)編程中的五種IO模型。下篇再繼續(xù) 對(duì) IO多路復(fù)用進(jìn)行深入的學(xué)習(xí)。
- Blocking IO - 阻塞IO
- NoneBlocking IO - 非阻塞IO
- IO multiplexing - IO多路復(fù)用
- signal driven IO - 信號(hào)驅(qū)動(dòng)IO
- asynchronous IO - 異步IO
Unix網(wǎng)絡(luò)編程中的五種IO模型
阻塞IO - Blocking IO
最傳統(tǒng)的一種IO模型,即在讀寫(xiě)數(shù)據(jù)過(guò)程中會(huì)發(fā)生阻塞現(xiàn)象。
當(dāng)用戶線程發(fā)出IO請(qǐng)求之后,內(nèi)核會(huì)去查看數(shù)據(jù)是否就緒,如果沒(méi)有就緒就會(huì)等待數(shù)據(jù)就緒,而用戶線程就會(huì)處于阻塞狀態(tài),用戶線程交出CPU。當(dāng)數(shù)據(jù)就緒之后,內(nèi)核會(huì)將數(shù)據(jù)拷貝到用戶線程,并返回結(jié)果給用戶線程,用戶線程才解除block狀態(tài)。
也許有人會(huì)說(shuō),可以采用多線程+ 阻塞IO 來(lái)解決效率問(wèn)題,但是由于在多線程 + 阻塞IO 中,每個(gè)socket對(duì)應(yīng)一個(gè)線程,這樣會(huì)造成很大的資源占用,并且尤其是對(duì)于長(zhǎng)連接來(lái)說(shuō),線程的資源一直不會(huì)釋放,如果后面陸續(xù)有很多連接的話,就會(huì)造成性能上的瓶頸。
非阻塞IO - NoneBlocking IO
當(dāng)用戶線程發(fā)起一個(gè) IO 操作后,并不需要等待,而是馬上就得到一個(gè)結(jié)果。如果結(jié)果是一個(gè) error 時(shí),它就知道數(shù)據(jù)還沒(méi)有準(zhǔn)備好,于是它可以再次發(fā)送 IO 操作。一旦內(nèi)核中的數(shù)據(jù)準(zhǔn)備好了,并且又再次收到了用戶線程的請(qǐng)求,那么它馬上就將數(shù)據(jù)拷貝到了用戶線程,然后返回。
在非阻塞IO 模型中,用戶線程需要不斷地詢問(wèn)內(nèi)核數(shù)據(jù)是否就緒,也就說(shuō)非阻塞IO不會(huì)交出CPU,而會(huì)一直占用CPU。
對(duì)于非阻塞IO就有一個(gè)非常嚴(yán)重的問(wèn)題,在while循環(huán)中需要不斷地去詢問(wèn)內(nèi)核數(shù)據(jù)是否就緒,這樣會(huì)導(dǎo)致CPU占用率非常高,因此一般情況下很少使用while循環(huán)這種方式來(lái)讀取數(shù)據(jù)。
- 非阻塞式主要體現(xiàn)在用戶進(jìn)程發(fā)起recvfrom系統(tǒng)調(diào)用的時(shí)候,這個(gè)時(shí)候系統(tǒng)內(nèi)核還沒(méi)有接收到數(shù)據(jù)報(bào),直接返回錯(cuò)誤給用戶進(jìn)程,告訴“當(dāng)前還沒(méi)有數(shù)據(jù)報(bào)可達(dá),晚點(diǎn)再來(lái)”
- 用戶進(jìn)程接收到信息,但是用戶進(jìn)程不知道什么時(shí)候數(shù)據(jù)報(bào)可達(dá),于是就開(kāi)始不斷輪詢(polling)向系統(tǒng)內(nèi)核發(fā)起recvfrom的系統(tǒng)調(diào)用“詢問(wèn)數(shù)據(jù)來(lái)了沒(méi)”,如果沒(méi)有則繼續(xù)返回錯(cuò)誤
- 用戶進(jìn)程輪詢發(fā)起recvfrom系統(tǒng)調(diào)用直至數(shù)據(jù)報(bào)可達(dá),這個(gè)時(shí)候需要等待系統(tǒng)內(nèi)核復(fù)制數(shù)據(jù)報(bào)到用戶進(jìn)程的緩沖區(qū),復(fù)制完成之后將返回成功提示
IO多路復(fù)用 - IO multiplexing
所謂 I/O 多路復(fù)用機(jī)制,就是說(shuō)通過(guò)一種機(jī)制,可以監(jiān)視多個(gè)描述符,一旦某個(gè)描述符就緒(一般是讀就緒或?qū)懢途w),能夠通知程序進(jìn)行相應(yīng)的讀寫(xiě)操作。這種機(jī)制的使用需要 select 、 poll 、 epoll 來(lái)配合。
在多路復(fù)用IO模型中,會(huì)有一個(gè)內(nèi)核線程不斷地去輪詢多個(gè) socket 的狀態(tài),只有當(dāng)真正讀寫(xiě)事件發(fā)送時(shí),才真正調(diào)用實(shí)際的IO讀寫(xiě)操作。因?yàn)樵诙嗦窂?fù)用IO模型中,只需要使用一個(gè)線程就可以管理多個(gè)socket,系統(tǒng)不需要建立新的進(jìn)程或者線程,也不必維護(hù)這些線程和進(jìn)程,并且只有真正有讀寫(xiě)事件進(jìn)行時(shí),才會(huì)使用IO資源,所以它大大減少來(lái)資源占用。
- IO復(fù)用模式是使用select或者poll函數(shù)向系統(tǒng)內(nèi)核發(fā)起調(diào)用,阻塞在這兩個(gè)系統(tǒng)函數(shù)調(diào)用,而不是真正阻塞于實(shí)際的IO操作(recvfrom調(diào)用才是實(shí)際阻塞IO操作的系統(tǒng)調(diào)用)
- 阻塞于select函數(shù)的調(diào)用,等待數(shù)據(jù)報(bào)套接字變?yōu)榭勺x狀態(tài)
- 當(dāng)select套接字返回可讀狀態(tài)的時(shí)候,就可以發(fā)起recvfrom調(diào)用把數(shù)據(jù)報(bào)復(fù)制到用戶空間的緩沖區(qū)
信號(hào)驅(qū)動(dòng)IO - signal driven IO
在信號(hào)驅(qū)動(dòng)IO模型中,當(dāng)用戶線程發(fā)起一個(gè)IO請(qǐng)求操作,會(huì)給對(duì)應(yīng)的socket注冊(cè)一個(gè)信號(hào)函數(shù),然后用戶線程會(huì)繼續(xù)執(zhí)行,當(dāng)內(nèi)核數(shù)據(jù)就緒時(shí)會(huì)發(fā)送一個(gè)信號(hào)給用戶線程,用戶線程接收到信號(hào)后,便在信號(hào)函數(shù)中調(diào)用IO讀寫(xiě)操作來(lái)進(jìn)行實(shí)際的IO請(qǐng)求操作。這個(gè)一般用于UDP中,對(duì)TCP套接字幾乎沒(méi)用,原因是該信號(hào)產(chǎn)生得過(guò)于頻繁,并且該信號(hào)的出現(xiàn)并沒(méi)有告訴我們發(fā)生了什么請(qǐng)求。
用戶進(jìn)程可以使用信號(hào)方式,當(dāng)系統(tǒng)內(nèi)核描述符就緒時(shí)將會(huì)發(fā)送SIGNO給到用戶空間,這個(gè)時(shí)候再發(fā)起recvfrom的系統(tǒng)調(diào)用等待返回成功提示,流程如下:
- 先開(kāi)啟套接字的信號(hào)IO啟動(dòng)功能,并通過(guò)一個(gè)內(nèi)置安裝信號(hào)處理函數(shù)的signaction系統(tǒng)調(diào)用,當(dāng)發(fā)起調(diào)用之后會(huì)直接返回;
- 其次,等待內(nèi)核從網(wǎng)絡(luò)中接收數(shù)據(jù)報(bào)之后,向用戶空間發(fā)送當(dāng)前數(shù)據(jù)可達(dá)的信號(hào)給信號(hào)處理函數(shù);
- 信號(hào)處理函數(shù)接收到信息就發(fā)起recvfrom系統(tǒng)調(diào)用等待內(nèi)核數(shù)據(jù)復(fù)制數(shù)據(jù)報(bào)到用戶空間的緩沖區(qū);
- 接收到復(fù)制完成的返回成功提示之后,應(yīng)用進(jìn)程就可以開(kāi)始從網(wǎng)絡(luò)中讀取數(shù)據(jù)。
異步IO - asynchronous IO
前面四種IO模型實(shí)際上都屬于同步IO,只有最后一種是真正的異步IO,因?yàn)闊o(wú)論是多路復(fù)用IO還是信號(hào)驅(qū)動(dòng)模型,IO操作的第2個(gè)階段都會(huì)引起用戶線程阻塞,也就是內(nèi)核進(jìn)行數(shù)據(jù)拷貝的過(guò)程都會(huì)讓用戶線程阻塞。
- 由POSIX規(guī)范定義,告知系統(tǒng)內(nèi)核啟動(dòng)某個(gè)操作,并讓內(nèi)核在整個(gè)操作包含數(shù)據(jù)等待以及數(shù)據(jù)復(fù)制過(guò)程的完成之后通知用戶進(jìn)程數(shù)據(jù)已經(jīng)準(zhǔn)備完成,可以進(jìn)行讀取數(shù)據(jù);
- 與上述的信號(hào)IO模型區(qū)分在于異步是通知我們何時(shí)IO操作完成,而信號(hào)IO是通知我們何時(shí)可以啟動(dòng)一個(gè)IO操作
總結(jié)
現(xiàn)代計(jì)算機(jī)服務(wù)器操作系統(tǒng)大部分都是基于linxu實(shí)現(xiàn),為處理高并發(fā)而采取NIO的模型,對(duì)于支持異步IO模型的系統(tǒng)持有不確定因素。
詳見(jiàn) BIO 、NIO 、AIO 總結(jié)
同步與異步的定義
- 同步:發(fā)起一個(gè)fn的調(diào)用,需要等待調(diào)用結(jié)果返回,該調(diào)用結(jié)果要么是期望的結(jié)果要么是異常拋出的結(jié)果,可以說(shuō)是原子性操作(要么成功要么失敗返回)
- 異步: 發(fā)起一個(gè)fn調(diào)用,無(wú)需等待結(jié)果就直接返回,只有當(dāng)被調(diào)用者執(zhí)行處理程序之后通過(guò)“喚醒”手段通知調(diào)用方獲取結(jié)果(喚醒的方式有回調(diào),事件通知等)
- 小結(jié): 同步和異步關(guān)注的是程序之間的通信
阻塞與非阻塞的定義
- 阻塞: 類比線程阻塞來(lái)說(shuō)明,在并發(fā)多線程爭(zhēng)搶資源的競(jìng)態(tài)條件下,如果有一個(gè)線程已持有鎖,那么當(dāng)前線程將無(wú)法獲取鎖而被掛起,處于等待狀態(tài)
- 非阻塞: 一旦線程釋放鎖,其他線程將會(huì)進(jìn)入就緒狀態(tài),具備爭(zhēng)搶鎖的資格
- 小結(jié): 阻塞與非阻塞更關(guān)注是程序等待結(jié)果的狀態(tài)
- 由此可知,同步異步與阻塞非阻塞之間不存在關(guān)聯(lián),關(guān)注的目標(biāo)是不一樣的
同步IO與異步IO(基于POSIX規(guī)范)
- 同步IO: 表示應(yīng)用進(jìn)程發(fā)起真實(shí)的IO操作請(qǐng)求(recvfrom)導(dǎo)致進(jìn)程一直處于等待狀態(tài),這時(shí)候進(jìn)程被阻塞,直到IO操作完成返回成功提示
- 異步IO: 表示應(yīng)用進(jìn)程發(fā)起真實(shí)的IO操作請(qǐng)求(recvfrom)導(dǎo)致進(jìn)程將直接返回一個(gè)錯(cuò)誤信息,“相當(dāng)于告訴進(jìn)程還沒(méi)有處理好,好了會(huì)通知你”
- 阻塞IO: 主要是體現(xiàn)發(fā)起IO操作請(qǐng)求通知內(nèi)核并且內(nèi)核接收到信號(hào)之后如果讓進(jìn)程等待,那么就是阻塞
- 非阻塞IO: 發(fā)起IO操作請(qǐng)求的時(shí)候不論結(jié)果直接告訴進(jìn)程“不用等待,晚點(diǎn)再來(lái)”,那就是非阻塞
IO模型對(duì)比
- 根據(jù)上述的同步與異步IO定義并結(jié)合上述的模型可知,只有異步IO模型符合POSIX規(guī)范的異步IO,其他IO模型都存在recvfrom系統(tǒng)調(diào)用被內(nèi)核阻塞,屬于同步IO操作
- 由此可知,阻塞IO與非阻塞IO可總結(jié)如下:
- 也就是說(shuō),要么稱為同步與異步IO,要么稱為上述5種模型的IO說(shuō)法,注意上述的同步與異步的概念
- 大部分操作系統(tǒng)都是基于同步IO的方式實(shí)現(xiàn),對(duì)于支持異步IO模型的操作系統(tǒng)還不確定,在實(shí)際工作我們經(jīng)常會(huì)說(shuō)Blocking-IO(阻塞IO)和Non-Blocking-IO(非阻塞IO),極少稱同步IO與異步IO
- 小結(jié): 同步與異步針對(duì)通信機(jī)制,阻塞與非阻塞針對(duì)程序調(diào)用等待結(jié)果的狀態(tài)
一句話總結(jié):
- 阻塞IO與非阻塞IO
這是最簡(jiǎn)單的模型,一般配合多線程來(lái)實(shí)現(xiàn)。
- 多路復(fù)用(select/poll/epoll)
一個(gè)線程解決多連接的問(wèn)題
- 信號(hào)驅(qū)動(dòng)IO模型
一種同步IO,更加靈活
- 異步IO模型
高效主流的模型,效率很高。