Linux五種IO模型的使用解讀
最近一直在研究Linux IO模型,Linux IO模型是一個(gè)挺復(fù)雜的概念,分析Linux IO模型一定要注意方法,否則會(huì)在一個(gè)個(gè)概念中,迷失自我。
1.IO模型簡(jiǎn)介
Linux IO模型是指Linux操作系統(tǒng)中用于實(shí)現(xiàn)輸入輸出的一種機(jī)制。
Linux IO模型主要分為五種:
阻塞IO、非阻塞IO、IO復(fù)用、信號(hào)驅(qū)動(dòng)IO和異步IO。
- 阻塞IO是最常見的IO模型,當(dāng)用戶進(jìn)程發(fā)起一個(gè)IO請(qǐng)求后,內(nèi)核會(huì)一直等待,直到IO操作完成并返回結(jié)果。在此期間,用戶進(jìn)程會(huì)被阻塞,無法進(jìn)行其他操作。
- 非阻塞IO是在阻塞IO的基礎(chǔ)上進(jìn)行改進(jìn)的一種IO模型。當(dāng)用戶進(jìn)程發(fā)起一個(gè)IO請(qǐng)求后,內(nèi)核會(huì)立即返回一個(gè)錯(cuò)誤碼,表示IO操作還未完成。用戶進(jìn)程可以繼續(xù)進(jìn)行其他操作,隨后再通過輪詢的方式來查詢IO操作是否完成。
- IO復(fù)用是指通過select、poll、epoll等系統(tǒng)調(diào)用來監(jiān)聽多個(gè)文件描述符的IO事件。當(dāng)某個(gè)文件描述符就緒時(shí),內(nèi)核會(huì)通知用戶進(jìn)程進(jìn)行IO操作。相比于阻塞IO和非阻塞IO,IO復(fù)用可以同時(shí)監(jiān)聽多個(gè)文件描述符,提高了IO效率。
- 信號(hào)驅(qū)動(dòng)IO是指用戶進(jìn)程通過signal或sigaction系統(tǒng)調(diào)用來注冊(cè)一個(gè)信號(hào)處理函數(shù),當(dāng)IO操作完成時(shí),內(nèi)核會(huì)向用戶進(jìn)程發(fā)送一個(gè)SIGIO信號(hào),用戶進(jìn)程在信號(hào)處理函數(shù)中進(jìn)行IO操作。相比于阻塞IO和非阻塞IO,信號(hào)驅(qū)動(dòng)IO可以避免用戶進(jìn)程被阻塞,提高了IO效率。
- 異步IO是指當(dāng)用戶進(jìn)程發(fā)起一個(gè)IO請(qǐng)求后,內(nèi)核會(huì)立即返回,表示IO操作已經(jīng)開始。當(dāng)IO操作完成后,內(nèi)核會(huì)通知用戶進(jìn)程,用戶進(jìn)程在此時(shí)才進(jìn)行IO操作。相比于其他IO模型,異步IO可以避免用戶進(jìn)程被阻塞,提高了IO效率。
2.五種IO模型
2.1 IO模型分析方法
分析IO模型需要了解2個(gè)問題:
問題1:發(fā)送IO請(qǐng)求,IO請(qǐng)求可以理解為用戶空間和內(nèi)核空間數(shù)據(jù)同步,根據(jù)發(fā)起者不同分為以下兩種情況:
- 由用戶程序發(fā)起(同步IO)。
- 由內(nèi)核發(fā)起(異步IO)。
問題2:等待數(shù)據(jù)到來,等待數(shù)據(jù)到來的方式有以下幾種:
- 阻塞(阻塞IO)。
- 輪詢(非阻塞IO)。
- 信號(hào)通知(信號(hào)驅(qū)動(dòng)IO)。

(內(nèi)核空間和用戶空間數(shù)據(jù)同步由誰發(fā)起是分析Linux IO模型最核心問題)
2.2 阻塞IO

阻塞IO/階段1
- 用戶程序調(diào)用recv函數(shù)發(fā)起IO請(qǐng)求(同步IO),讀取socket緩沖區(qū)數(shù)據(jù)。
- 由于socket緩沖區(qū)沒有就緒數(shù)據(jù)包,進(jìn)程狀態(tài)將從TASK_RUNNING切換至TASK_INTERRUPTIBLE狀態(tài),并通過進(jìn)程調(diào)度完成進(jìn)程阻塞。
- 同時(shí)進(jìn)程也會(huì)加入到socket等待隊(duì)列,等待數(shù)據(jù)到來回被喚醒。
注意:阻塞IO并不是阻塞CPU,而是進(jìn)行進(jìn)程狀態(tài)切換出讓CPU。

阻塞IO/階段2
- 網(wǎng)卡收到數(shù)據(jù)包后,通過DMA機(jī)制將數(shù)據(jù)包拷貝到內(nèi)核空間RingBuffer,并通過中斷機(jī)制將數(shù)據(jù)包拷貝至socket接收緩沖區(qū).
- socket接收到數(shù)據(jù)包喚醒等待隊(duì)列休眠進(jìn)程,進(jìn)程被喚醒后繼續(xù)完成用戶空間和內(nèi)核空間數(shù)據(jù)同步,recv函數(shù)成功返回。
2.3 非阻塞IO

阻塞IO/階段1
- 用戶程序調(diào)用recv函數(shù)發(fā)起IO請(qǐng)求,讀取socket緩沖區(qū)數(shù)據(jù)。
- 由于socket緩沖區(qū)沒有就緒數(shù)據(jù)包,非阻塞IO recv直接返回EWOULDBLOCK錯(cuò)誤碼,用戶如果一直調(diào)用recv函數(shù)則一直返回EWOULDBLOCK錯(cuò)誤碼,直到數(shù)據(jù)準(zhǔn)備好。
非阻塞IO/階段2
非阻塞IO和阻塞IO階段2相同。
2.4 IO復(fù)用
IO復(fù)用簡(jiǎn)介
IO復(fù)用是一種高效的IO處理方式,它可以讓一個(gè)進(jìn)程同時(shí)監(jiān)視多個(gè)文件描述符,當(dāng)其中任意一個(gè)文件描述符就緒時(shí),就可以進(jìn)行相應(yīng)的IO操作。
相比于傳統(tǒng)的阻塞IO和非阻塞IO,IO復(fù)用可以大大提高IO效率,減少CPU資源的浪費(fèi)。
在Linux中,常用的IO復(fù)用模型有select、poll、epoll等。
IO復(fù)用模型IO請(qǐng)求由用戶程序發(fā)起,所以IO復(fù)用模型為同步IO。
2.4.1 IO復(fù)用select模型

select模型通過位圖實(shí)現(xiàn)IO復(fù)用,將socket注冊(cè)到讀,寫,或異常位圖,通過select系統(tǒng)調(diào)用輪詢位圖,獲取socket事件,成功獲取到socket事件后,select成功返回,此時(shí)可以通過接收函數(shù)讀取socket緩沖區(qū)數(shù)據(jù)。
2.4.2 IO復(fù)用poll模型

poll模型和select非常相似,主要區(qū)別為poll模型把位圖改成鏈表,poll通過鏈表實(shí)現(xiàn)IO復(fù)用,將socket注冊(cè)poll_list鏈表,通過poll系統(tǒng)調(diào)用輪詢鏈表,獲取socket事件,成功獲取到socket事件后,poll成功返回,此時(shí)可以通過接收函數(shù)讀取socket緩沖區(qū)數(shù)據(jù)。
2.4.3 IO復(fù)用epoll模型

epoll模式采用回調(diào)方式獲取就緒socket事件,相比于select和poll模型的輪詢方式效率更高,通過epoll_ctl系統(tǒng)調(diào)用注冊(cè)socket事件至紅黑樹,當(dāng)socket接收到數(shù)據(jù)后通過回調(diào)函數(shù)將socket事件添加至就緒隊(duì)列,調(diào)用epoll_wait查詢就緒隊(duì)列就能獲取到socket事件。
2.5 信號(hào)驅(qū)動(dòng)IO

信號(hào)驅(qū)動(dòng)IO是基于SIGIO信號(hào)實(shí)現(xiàn)。
信號(hào)驅(qū)動(dòng)IO主要由三個(gè)步驟組成:
- 步驟1:用戶程序注冊(cè)SIGIO處理函數(shù)。
- 步驟2:內(nèi)核收到數(shù)據(jù)后,發(fā)送SIGO信號(hào),執(zhí)行SIGO信號(hào)處理函數(shù)。
- 步驟3:用戶程序調(diào)用recv函數(shù)發(fā)起IO請(qǐng)求,完成用戶空間和內(nèi)核空間數(shù)據(jù)同步。
信號(hào)驅(qū)動(dòng)IO是由用戶程序發(fā)起IO請(qǐng)求,所以信號(hào)驅(qū)動(dòng)IO屬于同步IO。
2.6 異步IO

異步IO實(shí)現(xiàn)的方式有很多種,上圖以POISX aio為例講解異步IO。
分析異步IO我們得抓住本質(zhì),異步IO的IO請(qǐng)求由內(nèi)核發(fā)起,只要滿足這一個(gè)點(diǎn)就是異步IO,不管是阻塞或者輪詢,或者信號(hào)通知,都不影響異步IO這個(gè)性質(zhì)。
3.IO模型常見問題?
問題1:阻塞IO和非阻塞IO區(qū)別?
a.阻塞IO
阻塞IO在沒有數(shù)據(jù)包時(shí),會(huì)通過阻塞進(jìn)程等待數(shù)據(jù)到來,阻塞進(jìn)程方法為:
- 設(shè)置進(jìn)程狀態(tài)為TASK_INTERRUPTIBLE。
- 通過進(jìn)程調(diào)度切換進(jìn)程,實(shí)現(xiàn)進(jìn)程阻塞。
進(jìn)程阻塞期間不會(huì)占用CPU資源。
b.非阻塞IO
非阻塞IO在沒有數(shù)據(jù)包時(shí),通過輪詢方式等待數(shù)據(jù)包到來,沒有數(shù)據(jù)包到來,調(diào)用接收函數(shù)會(huì)立即返回EWOULDBLOCK錯(cuò)誤碼,一直調(diào)用接收函數(shù),一直返回EWOULDBLOCK錯(cuò)誤碼。
非阻塞方式采用輪詢方式會(huì)一直占用CPU資源。
問題2:同步IO和異步IO區(qū)別?
區(qū)別同步IO和異步IO方法很簡(jiǎn)單,就是用戶空間和內(nèi)核空間數(shù)據(jù)同步由誰發(fā)起。
- 由用戶程序發(fā)起則是同步IO。
- 由內(nèi)核發(fā)起則是異步IO。
問題3:信號(hào)驅(qū)動(dòng)IO和異步IO區(qū)別?
信號(hào)驅(qū)動(dòng)IO和異步IO雖然實(shí)現(xiàn)流程有很多相似的地方,比如:注冊(cè)信號(hào)處理函數(shù),發(fā)送通知信號(hào),不會(huì)阻塞進(jìn)程等。
信號(hào)驅(qū)動(dòng)IO和異步IO有以下幾點(diǎn)區(qū)別:
- 信號(hào)驅(qū)動(dòng)IO屬于同步IO。
- 信號(hào)驅(qū)動(dòng)IO發(fā)送信號(hào)通知用戶程序發(fā)起IO請(qǐng)求。
- 異步IO發(fā)送信號(hào)通知用戶程序IO請(qǐng)求已由內(nèi)核發(fā)起并完成。
問題4:非阻塞IO是不是異步IO?
很多同學(xué)經(jīng)常把非阻塞IO和異步IO關(guān)聯(lián)在一起,其實(shí)這個(gè)是很錯(cuò)誤的一個(gè)想法,同步IO和異步IO都可能會(huì)阻塞進(jìn)程,同步IO和異步IO本質(zhì)區(qū)別為用戶空間和內(nèi)核空間數(shù)據(jù)同步由誰發(fā)起。
非阻塞IO的IO請(qǐng)求由用戶程序發(fā)起,屬于同步IO。
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
linux下查看本機(jī)和遠(yuǎn)程服務(wù)器的端口是否連通的方法
今天小編就為大家分享一篇linux下查看本機(jī)和遠(yuǎn)程服務(wù)器的端口是否連通的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2019-07-07
Linux解決RocketMQ中NameServer啟動(dòng)問題的方法詳解
這篇文章主要為大家詳細(xì)介紹了Linux解決RocketMQ中NameServer啟動(dòng)問題的方法,文中通過圖片和示例代碼進(jìn)行了詳細(xì)講解,需要的小伙伴可以參考下2023-08-08
Ubuntu基礎(chǔ)教程之a(chǎn)pt-get命令
這篇文章主要給大家介紹了關(guān)于Ubuntu基礎(chǔ)教程之a(chǎn)pt-get命令的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Ubuntu系統(tǒng)具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08
linux兩臺(tái)服務(wù)器實(shí)現(xiàn)自動(dòng)同步文件
這篇文章主要介紹了linux兩臺(tái)服務(wù)器實(shí)現(xiàn)自動(dòng)同步文件,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-08-08
apache啟用gzip壓縮的實(shí)現(xiàn)方法
對(duì)于部署在Linux服務(wù)器上的PHP程序,在服務(wù)器支持的情況下,我們建議你開啟使用Gzip Web壓縮,以前腳本之家介紹了iis中的開啟方法,這篇文章主要介紹了linux中apache的開啟方法2013-06-06
詳解CentOS 6.4 添加永久靜態(tài)路由所有方法匯總
這篇文章主要介紹了詳解CentOS 6.4 添加永久靜態(tài)路由所有方法匯總,非常具有實(shí)用價(jià)值,需要的朋友可以參考下。2016-12-12
關(guān)于Windows 不能在 本地計(jì)算器 啟動(dòng) Apache2(phpstudy)
今天在自己的本子上準(zhǔn)備放多個(gè)虛擬站點(diǎn)。用的是#phpstudy#。在軟件自身的站點(diǎn)設(shè)置中,根據(jù)提示添加的多站點(diǎn)無效不知道是否和我的系統(tǒng)是Win7有關(guān)2012-09-09

