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

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

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

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

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

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

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

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

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

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

