摘要:上圖中,進程調(diào)用了,系統(tǒng)函數(shù)在有數(shù)據(jù)報到達并已經(jīng)拷貝到應(yīng)用程序緩沖區(qū)時,或者有錯誤發(fā)生時才會返回最常見的錯誤是被信號中斷。多路復(fù)用在多路復(fù)用模型,我們會阻塞在這些系統(tǒng)函數(shù)中,而不是阻塞在真正的調(diào)用上。
文章還會涉及到同步 I/O,異步 I/O,阻塞 I/O 和非阻塞 I/O
首先我們需要理解以下概念:
在現(xiàn)在操作系統(tǒng)中,CPU通常會在兩種不同的模式下工作:
內(nèi)核態(tài)此模式下,程序代碼能夠完全,無限制地訪問底層硬件,能夠執(zhí)行任意的 CPU 指令和訪問任意的內(nèi)存地址。內(nèi)核模式通常留給最底層的,受信任的系統(tǒng)函數(shù)來使用。程序在內(nèi)核模式下崩潰是災(zāi)難性的,這甚至可以使整臺 PC 宕機。
用戶態(tài)在用戶模式下,程序代碼不能夠直接訪問硬件和內(nèi)存。執(zhí)行在用戶態(tài)的代碼必須委托系統(tǒng)函數(shù)去訪問硬件和內(nèi)存。因為有這種隔離機制的保護,程序在用戶態(tài)下崩潰通常是可恢復(fù)的。PC 中大多數(shù)程序也是在用戶態(tài)下執(zhí)行。
進程切換指操作系統(tǒng)進程調(diào)度切換,從某個進程到另外的進程。切換過程需要保存當前進程的所有狀態(tài),包括寄存器狀態(tài),關(guān)聯(lián)的內(nèi)核狀態(tài),虛擬內(nèi)存的配置等,具體會經(jīng)歷以下幾個步驟:
保存處理器上下文,包括程序計數(shù)器和其他寄存器
更新進程控制塊 (PCB)
移動進程的 PCB 到合適的隊列,例如就緒隊列,事件阻塞隊列
選擇其他進程,并更新他的 PCB
更新內(nèi)存數(shù)據(jù)結(jié)構(gòu)
恢復(fù) PCB 上下文
進程阻塞一個阻塞的進程通常是在等待某個事件,例如信號的釋放或者消息的到達。在多任務(wù)的系統(tǒng)中,阻塞的進程會通過系統(tǒng)調(diào)用去通知調(diào)度器自己處于 wait 的狀態(tài),以便能夠被移除出時序隊列。進程如果在 wait 狀態(tài)下還霸占 CPU 繼續(xù)執(zhí)行,這被稱為 busy-waiting (空等?)。顯然這是不合理的,因為他浪費了 CPU 時鐘周期,這原本可以被其他進程使用。所以當一個進程進入了阻塞狀態(tài),不應(yīng)繼續(xù)占用 CPU 資源。
緩沖式 I/O當我們寫數(shù)據(jù)(到文件系統(tǒng)),I/O 系統(tǒng)會累積數(shù)據(jù)到一個中間緩沖區(qū),當緩沖區(qū)積累到足夠數(shù)據(jù)時(或者調(diào)用flush())才會把數(shù)據(jù)發(fā)送到文件系統(tǒng),這樣減少了文件系統(tǒng)的訪問次數(shù)。因為對文件系統(tǒng)(磁盤)的訪問通常來說開銷很大(對比內(nèi)存間的拷貝),緩沖式 I/O 能夠有效提高性能,尤其是那種多次的小數(shù)據(jù)量寫操作。若是大數(shù)據(jù)量的寫操作,非緩沖式 I/O 會更好,因為緩沖式 I/O 并不會顯著減少(對文件系統(tǒng))系統(tǒng)調(diào)用,卻引入的額外的內(nèi)存拷貝工作,這些數(shù)據(jù)拷貝操作帶來了更高的 CPU 和內(nèi)存開銷。
文件描述符 (FD)在 Unix 及其衍生的操作系統(tǒng)中,文件描述符 (FD) 是一個抽象的指示符 (原文:indicator, handle,多數(shù)文章翻譯成句柄),用來訪問文件或其他 I/O 資源,例如管道,socket等。FD 是 POSIX 編程接口的一部分,是個非負索引值,許多底層的程序都會使用到 FD
I/O 模型當一個讀操作發(fā)生,會經(jīng)歷以下兩個階段:
數(shù)據(jù)準備階段 —— 例如等待網(wǎng)絡(luò)數(shù)據(jù)到達,當數(shù)據(jù)包到達時,他們會拷貝到內(nèi)核緩沖區(qū)中
數(shù)據(jù)轉(zhuǎn)移階段 —— 把數(shù)據(jù)從內(nèi)核拷貝到用戶進程
因為這兩個階段的存在,Linux 提供了以下5種 I/O 模型
Blocking I/O Model —— 阻塞式 I/O阻塞式 I/O 是最常見的 I/O 模式,默認地,所有的 socket 都是阻塞式的
這里我們用 UDP 協(xié)議和 recvfrom 系統(tǒng)調(diào)用來舉例。上圖中,進程調(diào)用了 recvfrom,系統(tǒng)函數(shù)在有數(shù)據(jù)報到達并已經(jīng)拷貝到應(yīng)用程序緩沖區(qū)時,或者有錯誤發(fā)生時才會返回(最常見的錯誤是被信號中斷)。我們認為進程在 recvfrom 從調(diào)用到返回的整個階段都被阻塞了。當 recvfrom 成功返回,應(yīng)用程序才會去處理數(shù)據(jù)報。
如果一個(數(shù)據(jù)準備階段) I/O 調(diào)用沒有完成,內(nèi)核會立即返回一個錯誤標記,而不是阻塞這個進程
第一次調(diào)用 recvfrom 時并沒有數(shù)據(jù)到達,于是內(nèi)核立即返回了錯誤標記 EWOULDBLOCK
第四次調(diào)用 recvfrom 時數(shù)據(jù)報已經(jīng)到達,拷貝到應(yīng)用程序緩沖區(qū)后,recvfrom 成功返回,之后程序會處理這些數(shù)據(jù)。
像這樣,程序在一個非阻塞的 FD 上循環(huán)調(diào)用 recvfrom 被稱為輪詢。這通常會浪費 CPU 時鐘周期,但這種模型也會偶爾使用到,例如一個系統(tǒng)只專注于某個功能的時候。
在 Linux I/O 多路復(fù)用模型,我們會阻塞在 select, poll, epoll 這些系統(tǒng)函數(shù)中,而不是阻塞在真正的 I/O 調(diào)用上。
上圖中,我們阻塞在 select() 函數(shù)上,等待 socket 數(shù)據(jù)可讀。select() 返回則表示 socket 數(shù)據(jù)可讀,之后我們才調(diào)用 recvfrom 拷貝數(shù)據(jù)到應(yīng)用程序緩沖區(qū)
缺點:這里我們使用了兩次系統(tǒng)調(diào)用 (select 和 recvfrom),而阻塞式 I/O 只使用了一次 recvfrom
優(yōu)勢:我們可以監(jiān)聽多個 FD 是否就緒
I/O 多路復(fù)用模型與阻塞式 I/O 模式非常相似。阻塞式 I/O 使用多線程(每個線程負責一個 FD)且每個線程都可以很自由地調(diào)用(阻塞式)系統(tǒng)函數(shù) recvfrom,而非使用 select 負責監(jiān)聽多個 FD。
Signal-Driven I/O Model —— 信號驅(qū)動 I/O告訴內(nèi)核當某個 FD 就緒時,釋放 SIGIO 信號來通知應(yīng)用程序
我們首先讓 socket 使用信號驅(qū)動 I/O 模式,并使用 sigaction 系統(tǒng)函數(shù)注冊一個信號處理器 (signal handler),該系統(tǒng)函數(shù)立即返回,這是非阻塞的。
當數(shù)據(jù)可讀,SIGIO 信號釋放出來被進程接收到,我們可以進行如下操作之一
在 signal handler 中調(diào)用 recvfrom 讀取數(shù)據(jù),隨后通知主循環(huán)數(shù)據(jù)已經(jīng)準備好了
signal handler 通知主循環(huán)去讀取數(shù)據(jù)
該模型的優(yōu)勢在于,等待數(shù)據(jù)到達的階段不會阻塞,主循環(huán)可以繼續(xù)執(zhí)行其他任務(wù)并等待 signal handler 的通知(數(shù)據(jù)可讀或可處理)
Asynchronous I/O Model —— 異步 I/O異步 I/O 模型告訴內(nèi)核執(zhí)行 I/O 操作,等到整個 I/O 操作(包括數(shù)據(jù)準備階段和數(shù)據(jù)轉(zhuǎn)移階段)完成后再通知我們。該模式跟信號驅(qū)動 I/O 非常相似,主要的區(qū)別是:信號驅(qū)動 I/O 中,內(nèi)核通知進程 I/O 操作可以開始(仍需把數(shù)據(jù)從內(nèi)核拷貝到進程),而異步 I/O 中內(nèi)核通知我們 I/O 操作已經(jīng)完成(數(shù)據(jù)已經(jīng)在進程緩沖區(qū)中)
我們調(diào)用了系統(tǒng)函數(shù) aio_read,向內(nèi)核傳遞了以下信息:
FD, 緩沖區(qū)指針,緩沖區(qū)大小
文件偏移量
I/O 執(zhí)行完畢的通知方式
aio_read 會立即返回,進程在等待 I/O 操作完成的整個階段都不會被阻塞。
I/O 模型的比較前面4種模型的主要區(qū)別在第一個階段(數(shù)據(jù)準備階段),第二階段(數(shù)據(jù)轉(zhuǎn)移階段)是一樣的:進程都阻塞在數(shù)據(jù)轉(zhuǎn)移階段(從內(nèi)核拷貝到應(yīng)用程序緩沖區(qū))。異步 I/O 內(nèi)核負責這兩個階段,不需要應(yīng)用程序干預(yù)。
同步 I/O 和異步 I/OPOSIX 定義如下
同步 I/O 會導(dǎo)致請求 I/O 操作的進程阻塞,直到 I/O 操作完成
異步 I/O 不會導(dǎo)致請求 I/O 操作的進程阻塞
根據(jù)這些定義,前面4種 I/O 模型 (blocking, nonblocking, I/O multiplexing, and signal-driven I/O) 都是同步 I/O,因為實際的 I/O 操作都會阻塞進程(舉例:信號驅(qū)動 I/O 在等待數(shù)據(jù)時非阻塞,但在數(shù)據(jù)轉(zhuǎn)移時阻塞了,是同步 I/O),只有異步 I/O 模型符合定義。
select, poll, epoll施工中
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/72735.html
摘要:阻塞請求結(jié)果返回之前,當前線程被掛起。也就是說在異步中,不會對用戶線程產(chǎn)生任何阻塞。當前線程在拿到此次請求結(jié)果的過程中,可以做其它事情。事實上,可以只用一個線程處理所有的通道。 準備知識 同步、異步、阻塞、非阻塞 同步和異步說的是服務(wù)端消息的通知機制,阻塞和非阻塞說的是客戶端線程的狀態(tài)。已客戶端一次網(wǎng)絡(luò)請求為例做簡單說明: 同步同步是指一次請求沒有得到結(jié)果之前就不返回。 異步請求不會...
閱讀 1035·2021-11-23 10:11
閱讀 3875·2021-11-16 11:50
閱讀 942·2021-10-14 09:43
閱讀 2727·2021-10-14 09:42
閱讀 2724·2021-09-22 16:02
閱讀 1072·2019-08-29 10:57
閱讀 3389·2019-08-29 10:57
閱讀 2285·2019-08-26 13:52