linux io指的是一種檔案操作;在Linux中,檔案就是一串二進位制流,那麼在資訊的交換過程中,我們都是對這些流進行資料收發操作,這些操作簡稱為I/O操作;由於Linux使用的是虛擬記憶體機制,所以必須通過系統呼叫請求核心來完成IO動作。
本教學操作環境:linux5.9.8系統、Dell G3電腦。
linux io指的是什麼?
我們都知道在Linux的世界,一切皆檔案。
而檔案就是一串二進位制流,不管Socket、FIFO、管道還是終端,對我們來說,一切都是流。
在資訊的交換過程中,我們都是對這些流進行資料收發操作,簡稱為I/O操作。
往流中讀取資料,系統呼叫Read,寫入資料,系統呼叫Write。
通常使用者程序的一個完整的IO分為兩個階段:
磁碟IO:
網路IO:
作業系統和驅動程式執行在核心空間,應用程式執行在使用者空間,兩者不能使用指標傳遞資料,因為Linux使用的虛擬記憶體機制,必須通過系統呼叫請求核心來完成IO動作。
IO有記憶體IO、網路IO和磁碟IO三種,通常我們說的IO指的是後兩者!
為什麼需要IO模型
如果使用同步的方式來通訊的話,所有的操作都在一個執行緒內順序執行完成,這麼做缺點是很明顯的:
因該需要出現IO模型。
在描述Linux IO模型之前,我們先來了解一下Linux系統資料讀取的過程:
以使用者請求index.html檔案為例子說明
使用者空間和核心空間
作業系統的核心是核心,獨立於普通的應用程式,可以存取受保護的記憶體空間,也有存取底層硬體裝置的所有許可權。
程序切換
為了控制程序的執行,核心必須有能力掛起正在CPU上執行的程序,並恢復以前掛起的某個程序的執行。
這種行為被稱為程序切換。
因此可以說,任何程序都是在作業系統核心的支援下執行的,是與核心緊密相關的。
程序的阻塞
正在執行的程序,由於期待的某些事件未發生,如請求系統資源失敗、等待某種操作的完成、新資料尚未到達或無新工作做等,則由系統自動執行阻塞原語(Block),使自己由執行狀態變為阻塞狀態。
可見,程序的阻塞是程序自身的一種主動行為,也因此只有處於執行態的程序(獲得CPU),才可能將其轉為阻塞狀態。
當程序進入阻塞狀態,是不佔用CPU資源的。
檔案描述符
檔案描述符(File Descriptor)是電腦科學中的一個術語,是一個用於表述指向檔案的參照的抽象化概念。
檔案描述符在形式上是一個非負整數,實際上,它是一個索引值,指向核心為每一個程序所維護的該程序開啟檔案的記錄表。
快取IO
大多數檔案系統的預設 IO 操作都是快取 IO。
其讀寫過程如下:
讀操作:作業系統檢查核心的緩衝區有沒有需要的資料,如果已經快取了,那麼就直接從快取中返回;否則從磁碟、網路卡等中讀取,然後快取在作業系統的快取中;
寫操作:將資料從使用者空間複製到核心空間的快取中。這時對使用者程式來說寫操作就已經完成,至於什麼時候再寫到磁碟、網路卡等中由作業系統決定,除非顯示地呼叫了 sync 同步命令。
假設核心空間快取無需要的資料,使用者程序從磁碟或網路讀資料分兩個階段:
階段一: 核心程式從磁碟、網路卡等讀取資料到核心空間快取區;
階段二: 使用者程式從核心空間快取拷貝資料到使用者空間。
快取 IO 的缺點:
資料在傳輸過程中需要在應用程式地址空間和核心空間進行多次資料拷貝操作,這些資料拷貝操作所帶來的CPU以及記憶體開銷非常大。
使用者空間的應用程式執行一個系統呼叫,這會導致應用程式阻塞,什麼也不幹,直到資料準備好,並且將資料從核心複製到使用者程序,最後程序再處理資料,在等待資料到處理資料的兩個階段,整個程序都被阻塞,不能處理別的網路IO。
這也是最簡單的IO模型,在通常FD較少、就緒很快的情況下使用是沒有問題的。
非阻塞的系統呼叫呼叫之後,程序並沒有被阻塞,核心馬上返回給程序,如果資料還沒準備好,此時會返回一個error。
程序在返回之後,可以乾點別的事情,然後再發起系統呼叫。
重複上面的過程,迴圈往復的進行系統呼叫。這個過程通常被稱之為輪詢。
輪詢檢查核心資料,直到資料準備好,再拷貝資料到程序,進行資料處理。
需要注意,拷貝資料整個過程,程序仍然是屬於阻塞的狀態。
這種方式在程式設計中對Socket設定O_NONBLOCK
即可。
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,在Unix中,為非同步方式存取檔案定義了一套庫函數,定義了AIO的一系列介面。
aio_read
或者aio_write
發起非同步IO操作,使用aio_error
檢查正在執行的IO操作的狀態。目前Linux中AIO的核心實現只對檔案IO有效,如果要實現真正的AIO,需要使用者自己來實現。
目前有很多開源的非同步IO庫,例如libevent、libev、libuv。
相關推薦:《》
以上就是linux io指的是什麼的詳細內容,更多請關注TW511.COM其它相關文章!