IO操作與IO模型

  • 2020 年 4 月 14 日
  • 筆記

一 、IO操作本質

數據複製的過程中不會消耗CPU

# 1 記憶體分為內核緩衝區和用戶緩衝區
# 2 用戶的應用程式不能直接操作內核緩衝區,需要將數據從內核拷貝到用戶才能使用
# 3 而IO操作、網路請求載入到記憶體的數據一開始是放在內核緩衝區的

二、 IO模型

  1. BIO – 阻塞模式I/O

用戶進程從發起請求,到最終拿到數據前,一直掛起等待; 數據會由用戶進程完成拷貝

'''
舉個例子:一個人去 商店買一把菜刀,
他到商店問老闆有沒有菜刀(發起系統調用)
如果有(表示在內核緩衝區有需要的數據)
老闆直接把菜刀給買家(從內核緩衝區拷貝到用戶緩衝區)
這個過程買家一直在等待

如果沒有,商店老闆會向工廠下訂單(IO操作,等待數據準備好)
工廠把菜刀運給老闆(進入到內核緩衝區)
老闆把菜刀給買家(從內核緩衝區拷貝到用戶緩衝區)
這個過程買家一直在等待
是同步io
'''
  1. NIO – 非阻塞模式I/O

用戶進程發起請求,如果數據沒有準備好,那麼立刻告知用戶進程未準備好;此時用戶進程可選擇繼續發起請求、或者先去做其他事情,稍後再回來繼續發請求,直到被告知數據準備完畢,可以開始接收為止; 數據會由用戶進程完成拷貝

'''
舉個例子:一個人去 商店買一把菜刀,
他到商店問老闆有沒有菜刀(發起系統調用)
老闆說沒有,在向工廠進貨(返回狀態)
買家去別地方玩了會,又回來問,菜刀到了么(發起系統調用)
老闆說還沒有(返回狀態)
買家又去玩了會(不斷輪詢)
最後一次再問,菜刀有了(數據準備好了)
老闆把菜刀遞給買家(從內核緩衝區拷貝到用戶緩衝區)

整個過程輪詢+等待:輪詢時沒有等待,可以做其他事,從內核緩衝區拷貝到用戶緩衝區需要等待
是同步io
'''
  1. IO Multiplexing – I/O多路復用模型

類似BIO,只不過找了一個代理,來掛起等待,並能同時監聽多個請求; 數據會由用戶進程完成拷貝

'''
舉個例子:多個人去 一個商店買菜刀,
多個人給老闆打電話,說我要買菜刀(發起系統調用)
老闆把每個人都記錄下來(放到select中)
老闆去工廠進貨(IO操作)
有貨了,再挨個通知買到的人,來取刀(通知/返回可讀條件)
買家來到商店等待,老闆把到給買家(從內核緩衝區拷貝到用戶緩衝區)

多路復用:老闆可以同時接受很多請求(select模型最大1024個,epoll模型),
但是老闆把到給買家這個過程,還需要等待,
是同步io
'''
  1. AIO – 非同步I/O模型

發起請求立刻得到回復,不用掛起等待; 數據會由內核進程主動完成拷貝

'''
舉個例子:還是買菜刀
現在是網上下單到商店(系統調用)
商店確認(返回)
商店去進貨(io操作)
商店收到貨把貨發個賣家(從內核緩衝區拷貝到用戶緩衝區)
買家收到貨(指定訊號)

整個過程無等待
非同步io
'''

三、同步I/O與非同步I/O

  • 同步I/O
    • 概念:導致請求進程阻塞的I/O操作,直到I/O操作任務完成
    • 類型:BIO、NIO、IO Multiplexing
  • 非同步I/O
    • 概念:不導致進程阻塞的I/O操作
    • 類型:AIO

注意:

  • 同步I/O與非同步I/O判斷依據是,是否會導致用戶進程阻塞
  • BIO中socket直接阻塞等待(用戶進程主動等待,並在拷貝時也等待)
  • NIO中將數據從內核空間拷貝到用戶空間時阻塞(用戶進程主動詢問,並在拷貝時等待)
  • IO Multiplexing中select等函數為阻塞、拷貝數據時也阻塞(用戶進程主動等待,並在拷貝時也等待)
  • AIO中從始至終用戶進程都沒有阻塞(用戶進程是被動的)

四、 IO設計模式

 Reactor模式,基於同步I/O實現
- Proactor模式,基於非同步I/O實現

Reactor模式通常採用IO多路復用機制進行具體實現

- kqueue、epoll、poll、select等機制

Proactor模式通常採用OS Asynchronous IO(AIO)的非同步機制進行實現

- 前提是對應作業系統支援AIO,比如支援非同步IO的linux(不太成熟)、具備IOCP的windows server(非常成熟)

Reactor模式和Proactor模式都是事件驅動,主要實現步驟:

  1. 事件註冊:將事件與事件處理器進行分離。將事件註冊到事件循環中,將事件處理器單獨管理起來,記錄其與事件的對應關係。
  2. 事件監聽:啟動事件循環,一旦事件已經就緒/完成,就立刻通知事件處理器
  3. 事件分發:當收到事件就緒/完成的訊號,便立刻激活與之對應的事件處理器
  4. 事件處理:在進程/執行緒/協程中執行事件處理器

使用過程中,用戶通常只負責定義事件和事件處理器並將其註冊以及一開始的事件循環的啟動,這個過程就會是以非同步的形式執行任務。

Reactor模式

Proactor模式

對比分析

Reactor模型處理耗時長的操作會造成事件分發的阻塞,影響到後續事件的處理;

Proactor模型實現邏輯複雜;依賴作業系統對非同步的支援,目前實現了純非同步操作的作業系統少,實現優秀的如windows IOCP,但由於其windows系統用於伺服器的局限性,目前應用範圍較小;而Unix/Linux系統對純非同步的支援有限,因而應用事件驅動的主流還是基於select/epoll等實現的reactor模式

Python中:如asyncio、gevent、tornado、twisted等非同步模組都是依據事件驅動模型設計,更多的都是使用reactor模型,其中部分也支援proactor模式,當然需要根據當前運行的作業系統環境來進行手動配置