I/O介紹 1)I/O(Input/Output),即輸入/輸出,分為IO設備和IO介面兩個部分 2)IO分類: • 網路IO:網路IO的本質是socket的讀取,socket在linux系統被抽象為流,IO可以理解為對流的操作 • 磁碟IO:文件的一次讀寫操作就會發生IO操作 3)IO操作類型: ...
I/O介紹
1)I/O(Input/Output),即輸入/輸出,分為IO設備和IO介面兩個部分
2)IO分類:
• 網路IO:網路IO的本質是socket的讀取,socket在linux系統被抽象為流,IO可以理解為對流的操作
• 磁碟IO:文件的一次讀寫操作就會發生IO操作
3)IO操作類型:
• 緩存IO:
- 基本介紹:又稱為標準IO、高級磁碟IO,遵循 ANSI C 相關標準,C標準庫中提供了標準IO庫,即stdio,其實現了跨平臺的用戶緩存解決方案,大多數文件系統的預設IO操作
- 工作機制:第一階段:等待數據準備就緒(內核將數據複製到內核記憶體);第二階段:真正IO操作的過程(進程將數據複製到進程記憶體)
◇ 對於磁碟IO來說:先將數據從磁碟複製到內核記憶體(也稱為文件系統的頁緩存),再從內核記憶體複製到進程記憶體
◇ 對於網路IO來說:等待網路上的數據分組到達,然後被覆制到內核記憶體,再把數據從內核記憶體複製到進程記憶體
- 缺點:數據在傳輸過程中需要在進程記憶體和內核記憶體進行多次數據拷貝操作,這些數據拷貝操作所帶來的CPU以及記憶體開銷是非常大的
• 直接IO:
- 基本介紹:又稱為文件IO、低級磁碟IO,遵循 POSIX 相關標準,任何相容POSIX標準的操作系統上都支持直接IO
- 工作機制:IO操作不經過內核記憶體,數據在磁碟/網卡和進程記憶體之間直接進行傳輸,所以其效率更高
• 其他:Linux中使用的是GLIBC,它是標準C庫的超集,不僅包含 ANSI C 中定義的函數,還包括POSIX標準中定義的函數,因此,Linux下既可以使用直接IO,也可以使用緩存IO
4)進程是基於串列模式處理多路IO(即多個IO)的,WEB進程通常要處理雙路IO:
• 網路IO
• 磁碟IO
同步與非同步 & 阻塞與非阻塞
1)同步與非同步關註的是消息通信機制,即【被調用者】【通過何種消息通知機制】【通知】【調用者】
• 同步(sync):系統調用發出後,被調用者不會立即返回消息,但一旦返回則返回的是最終調用結果;同步會導致請求進程阻塞,直到IO操作完成
• 非同步(async):系統調用發出後,被調用者會立即返回消息,但返回的並非調用結果,而是告知調用者調用已收到、回去等待;被調用者通過狀態、通知機制等告知調用者或通過回調函數來處理結果;非同步不會導致請求進程阻塞
2)阻塞與非阻塞關註的是等待調用結果時的狀態,即【調用者】【等待被調用者返回調用結果】【時的狀態】
• 阻塞(block):在調用結果返回前,調用者會被掛起(轉為不可中斷式睡眠狀態),調用者只有在得到調用結果之後才能繼續工作,在被阻塞期間無法響應任何信號
• 非阻塞(unblock):在調用結果返回前,調用者不會被掛起,其可以進行其他操作,即調用不會阻塞調用者
I/O模型
1、基本介紹
1)阻塞式IO(Blocking IO):同步
• 基本介紹:
- 最常用的一個模型,也是最簡單的模型,在Linux中,預設情況下所有的socket都是blocking的
• 工作機制:
- 第一階段(等待數據準備就緒):阻塞,等待數據準備完成
- 第二階段(真正IO操作過程):阻塞,等待數據從內核記憶體複製到進程記憶體
• 優點:
- 能夠及時返回數據,無延遲
- 開發實現簡單容易
• 缺點:
- 效率低
2)非阻塞式IO(Unblocking IO):同步
• 工作機制:
- 第一階段(等待數據準備就緒):非阻塞,忙等待狀態,即進程發起IO操作後,在此階段進程不會被阻塞,其可以進行其他操作,但會隔指定時間來查看調用結果
- 第二階段(真正IO操作過程):阻塞,等待數據從內核記憶體複製到進程記憶體
• 優點:
- 在等待數據準備階段,可以進行其他操作
• 缺點:
- 忙等待會導致整體數據吞吐量的降低,效率不一定比同步阻塞式IO高
3)IO復用(IO Multiplexing):同步
• 基本介紹:
- 通常情況下,進程在同一時間內僅能處理一個IO,IO復用就是通過一種特殊的IO調用來實現同時處理多個IO
• 工作機制:
- 第一階段(等待數據準備就緒):阻塞,進程將IO調用請求發給代理(select、poll等)處理,代理去發起真正的IO調用。在IO處理工程中,代理處於空閑狀態,進程如果沒有其他IO操作請求,則會阻塞在代理處;如果有其他IO請求,則可以繼續接收,並將其交由代理處理。其本質還是阻塞的,只是阻塞在了代理上,而非阻塞在了真正的系統調用上
- 第二階段(真正IO操作過程):阻塞,等待數據從內核記憶體複製到進程記憶體
• 實現:
- select(BSD風格):
◇ 最多不能超過1024個IO請求,httpd-prefork模式僅能支持1024的最大併發就是因為其使用的是select模型,httpd-worker模式是基於線程來調用select()
◇ 內核每準備好一個IO,select()就會掃描一遍所管理的數據結構,然後將其輸出至用戶空間,進程看到IO完成,則會響應給用戶
- poll(SysV風格)
• 因為還是阻塞,所以性能上沒有提升,但處理併發的能力有所提升
4)事件驅動式IO(Signal Driven I/O):非同步
• 工作機制:
- 第一階段(等待數據準備就緒):非阻塞,進程發起IO操作,內核收到請求後會立即通知進程數據準備中,進程無需等待數據準備完成,也無需忙等待,可以去進行其他操作,準備完成後,內核會通知進程(事件指的就是數據準備完成)
- 第二階段(真正IO操作過程):阻塞,等待數據從內核記憶體複製到進程記憶體。但是,如果其它已完成第一階段的IO操作,在通知此處於阻塞狀態的進程時,其是無法響應的,然後只能通過回調函數將數據從內核記憶體複製到進程記憶體
• 事件驅動的消息通知機制:
- 水平觸發:多次通知,直至響應;可靠、但浪費資源
- 邊緣觸發:通知一次,如在指定時間內未取走資源,則稍後進程會通過回調函數來取走資源
• 實現:
- epoll(Linux):httpd-event模式(據說2.4版本支持非同步IO)就是通過此模型來處理高併發,NGINX設計之初就是基於事件驅動模型+邊緣觸發機制
- kqueue(FreeBSD)
- /dev/poll(Solaris)
5)非同步IO(Asynchronous IO):非同步
• 工作機制:
- 第一階段(等待數據準備就緒):非阻塞,進程發起IO操作,內核通知進程數據準備中,進程無需等待數據準備完成,也無需忙等待,可以去進行其他操作,數據在此期間被覆制到內核記憶體
- 第二階段(真正IO操作過程):非阻塞,數據從內核記憶體複製到進程記憶體,然後通知進程來取,進程直接將數據封裝為響應報文,返回用戶
• NGINX也支持非同步IO和記憶體映射機制
2、相關圖示
工作模型
1)單進程
• 阻塞、串列
2)多進程
• 需要大量的進程切換,資源浪費在切換上
• 每個進程的記憶體地址空間是獨立的,當多個用戶訪問同一資源時,此資源會載入至多個進程記憶體地址空間,造成記憶體空間的數據重覆,降低了記憶體的利用率
3)線程(Linux不支持原生線程,而是輕量級進程,其依賴於線程庫)
• 分為單進程多線程和多進程多線程
• 線程共用所在進程的記憶體地址空間,對記憶體的需求較之進程略有下降
• 快速切換時會帶來線程抖動
• 註意:單顆CPU下的線程模式,幾乎沒有優勢
- 指令區:存放指令
- 堆區:存放文件
- 棧區:存放變數
• 每個線程響應一個請求,線程依然需要切換,但屬於輕量級切換
• 每個線程響應多個請求,如果其中一個請求進行了IO操作,必然引起阻塞,那麼其他請求就會被中斷,所以在此情況下, 不能讓IO阻塞