共計 3184 個字符,預計需要花費 8 分鐘才能閱讀完成。
本篇內容主要講解“linux io 是什么”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓丸趣 TV 小編來帶大家學習“linux io 是什么”吧!
linux io 指的是一種文件操作;在 Linux 中,文件就是一串二進制流,那么在信息的交換過程中,我們都是對這些流進行數據收發操作,這些操作簡稱為 I / O 操作;由于 Linux 使用的是虛擬內存機制,所以必須通過系統調用請求內核來完成 IO 動作。
linux io 指的是什么?
我們都知道在 Linux 的世界,一切皆文件。
而文件就是一串二進制流,不管 Socket、FIFO、管道還是終端,對我們來說,一切都是流。
在信息的交換過程中,我們都是對這些流進行數據收發操作,簡稱為 I / O 操作。
往流中讀取數據,系統調用 Read,寫入數據,系統調用 Write。
通常用戶進程的一個完整的 IO 分為兩個階段:
磁盤 IO:
網絡 IO:
操作系統和驅動程序運行在內核空間,應用程序運行在用戶空間,兩者不能使用指針傳遞數據,因為 Linux 使用的虛擬內存機制,必須通過系統調用請求內核來完成 IO 動作。
IO 有內存 IO、網絡 IO 和磁盤 IO 三種,通常我們說的 IO 指的是后兩者!
為什么需要 IO 模型
如果使用同步的方式來通信的話,所有的操作都在一個線程內順序執行完成,這么做缺點是很明顯的:
因為同步的通信操作會阻塞同一個線程的其他任何操作,只有這個操作完成了之后,后續的操作才可以完成,所以出現了同步阻塞 + 多線程(每個 Socket 都創建一個線程對應),但是系統內線程數量是有限制的,同時線程切換很浪費時間,適合 Socket 少的情況。
因該需要出現 IO 模型。
Linux 的 IO 模型
在描述 Linux IO 模型之前,我們先來了解一下 Linux 系統數據讀取的過程:
以用戶請求 index.html 文件為例子說明
基本概念
用戶空間和內核空間
操作系統的核心是內核,獨立于普通的應用程序,可以訪問受保護的內存空間,也有訪問底層硬件設備的所有權限。
為了保證內核的安全,用戶進程不能直接操作內核,操作系統將虛擬空間劃分為兩部分,一部分為內核空間,一部分為用戶空間。
進程切換
為了控制進程的執行,內核必須有能力掛起正在 CPU 上運行的進程,并恢復以前掛起的某個進程的執行。
這種行為被稱為進程切換。
因此可以說,任何進程都是在操作系統內核的支持下運行的,是與內核緊密相關的。
進程的阻塞
正在執行的進程,由于期待的某些事件未發生,如請求系統資源失敗、等待某種操作的完成、新數據尚未到達或無新工作做等,則由系統自動執行阻塞原語 (Block),使自己由運行狀態變為阻塞狀態。
可見,進程的阻塞是進程自身的一種主動行為,也因此只有處于運行態的進程(獲得 CPU),才可能將其轉為阻塞狀態。
當進程進入阻塞狀態,是不占用 CPU 資源的。
文件描述符
文件描述符(File Descriptor)是計算機科學中的一個術語,是一個用于表述指向文件的引用的抽象化概念。
文件描述符在形式上是一個非負整數,實際上,它是一個索引值,指向內核為每一個進程所維護的該進程打開文件的記錄表。
當程序打開一個現有文件或者創建一個新文件時,內核向進程返回一個文件描述符。
緩存 IO
大多數文件系統的默認 IO 操作都是緩存 IO。
其讀寫過程如下:
讀操作:操作系統檢查內核的緩沖區有沒有需要的數據,如果已經緩存了,那么就直接從緩存中返回;否則從磁盤、網卡等中讀取,然后緩存在操作系統的緩存中;
寫操作:將數據從用戶空間復制到內核空間的緩存中。這時對用戶程序來說寫操作就已經完成,至于什么時候再寫到磁盤、網卡等中由操作系統決定,除非顯示地調用了 sync 同步命令。
假設內核空間緩存無需要的數據,用戶進程從磁盤或網絡讀數據分兩個階段:
階段一:內核程序從磁盤、網卡等讀取數據到內核空間緩存區;
階段二:用戶程序從內核空間緩存拷貝數據到用戶空間。
緩存 IO 的缺點:
數據在傳輸過程中需要在應用程序地址空間和內核空間進行多次數據拷貝操作,這些數據拷貝操作所帶來的 CPU 以及內存開銷非常大。
同步阻塞
用戶空間的應用程序執行一個系統調用,這會導致應用程序阻塞,什么也不干,直到數據準備好,并且將數據從內核復制到用戶進程,最后進程再處理數據,在等待數據到處理數據的兩個階段,整個進程都被阻塞,不能處理別的網絡 IO。
調用應用程序處于一種不再消費 CPU 而只是簡單等待響應的狀態,因此從處理的角度來看,這是非常有效的。
這也是最簡單的 IO 模型,在通常 FD 較少、就緒很快的情況下使用是沒有問題的。
同步非阻塞
非阻塞的系統調用調用之后,進程并沒有被阻塞,內核馬上返回給進程,如果數據還沒準備好,此時會返回一個 error。
進程在返回之后,可以干點別的事情,然后再發起系統調用。
重復上面的過程,循環往復的進行系統調用。這個過程通常被稱之為輪詢。
輪詢檢查內核數據,直到數據準備好,再拷貝數據到進程,進行數據處理。
需要注意,拷貝數據整個過程,進程仍然是屬于阻塞的狀態。
這種方式在編程中對 Socket 設置 O_NONBLOCK 即可。
IO 多路復用
IO 多路復用,這是一種進程預先告知內核的能力,讓內核發現進程指定的一個或多個 IO 條件就緒了,就通知進程。
使得一個進程能在一連串的事件上等待。
IO 復用的實現方式目前主要有 Select、Poll 和 Epoll。
偽代碼描述 IO 多路復用:
while(status == OK) {// 不斷輪詢 ready_fd_list = io_wait(fd_list); // 內核緩沖區是否有準備好的數據 for(fd in ready_fd_list) { data = read(fd) // 有準備好的數據讀取到用戶緩沖區 process(data) }}
信號驅動
首先我們允許 Socket 進行信號驅動 IO,并安裝一個信號處理函數,進程繼續運行并不阻塞。
當數據準備好時,進程會收到一個 SIGIO 信號,可以在信號處理函數中調用 I / O 操作函數處理數據。
流程如下:
開啟套接字信號驅動 IO 功能
系統調用 Sigaction 執行信號處理函數(非阻塞,立刻返回)
數據就緒,生成 Sigio 信號,通過信號回調通知應用來讀取數據
此種 IO 方式存在的一個很大的問題:Linux 中信號隊列是有限制的,如果超過這個數字問題就無法讀取數據
異步非阻塞
異步 IO 流程如下所示:
當用戶線程調用了 aio_read 系統調用,立刻就可以開始去做其它的事,用戶線程不阻塞
內核就開始了 IO 的第一個階段:準備數據。當內核一直等到數據準備好了,它就會將數據從內核內核緩沖區,拷貝到用戶緩沖區
內核會給用戶線程發送一個信號,或者回調用戶線程注冊的回調接口,告訴用戶線程 Read 操作完成了
用戶線程讀取用戶緩沖區的數據,完成后續的業務操作
相對于同步 IO,異步 IO 不是順序執行。
用戶進程進行 aio_read 系統調用之后,無論內核數據是否準備好,都會直接返回給用戶進程,然后用戶態進程可以去做別的事情。
等到數據準備好了,內核直接復制數據給進程,然后從內核向進程發送通知。
對比信號驅動 IO,異步 IO 的主要區別在于:
信號驅動由內核告訴我們何時可以開始一個 IO 操作 (數據在內核緩沖區中),而異步 IO 則由內核通知 IO 操作何時已經完成 (數據已經在用戶空間中)。
異步 IO 又叫做事件驅動 IO,在 Unix 中,為異步方式訪問文件定義了一套庫函數,定義了 AIO 的一系列接口。
使用 aio_read 或者 aio_write 發起異步 IO 操作,使用 aio_error 檢查正在運行的 IO 操作的狀態。
目前 Linux 中 AIO 的內核實現只對文件 IO 有效,如果要實現真正的 AIO,需要用戶自己來實現。
目前有很多開源的異步 IO 庫,例如 libevent、libev、libuv。
到此,相信大家對“linux io 是什么”有了更深的了解,不妨來實際操作一番吧!這里是丸趣 TV 網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!