從核心檔案系統看檔案讀寫過程
系統呼叫
作業系統的主要功能是為管理硬體資源和為應用程式開發人員提供良好的環境,但是計算機系統的各種硬體資源是有限的,因此為了保證每一個程序都能安全的執行。處理器設有兩種模式:“使用者模式”與“核心模式”。一些容易發生安全問題的操作都被限制在只有核心模式下才可以執行,例如I/O操作,修改基址暫存器內容等。而連線使用者模式和核心模式的介面稱之為系統呼叫。
應用程式程式碼執行在使用者模式下,當應用程式需要實現核心模式下的指令時,先向作業系統傳送呼叫請求。作業系統收到請求後,執行系統呼叫介面,使處理器進入核心模式。當處理器處理完系統呼叫操作後,作業系統會讓處理器返回使用者模式,繼續執行使用者程式碼。
程序的虛擬地址空間可分為兩部分,核心空間和使用者空間。核心空間中存放的是核心程式碼和資料,而程序的使用者空間中存放的是使用者程式的程式碼和資料。不管是核心空間還是使用者空間,它們都處於虛擬空間中,都是對實體地址的對映。
應用程式中實現對檔案的操作過程就是典型的系統呼叫過程。
回到頂部虛擬檔案系統
一個作業系統可以支援多種底層不同的檔案系統(比如NTFS, FAT, ext3, ext4),為了給核心和使用者程序提供統一的檔案系統檢視,Linux在使用者程序和底層檔案系統之間加入了一個抽象層,即虛擬檔案系統(Virtual File System, VFS),程序所有的檔案操作都通過VFS,由VFS來適配各種底層不同的檔案系統,完成實際的檔案操作。
通俗的說,VFS就是定義了一個通用檔案系統的介面層和適配層,一方面為使用者程序提供了一組統一的訪問檔案,目錄和其他物件的統一方法,另一方面又要和不同的底層檔案系統進行適配。如圖所示:
虛擬檔案系統主要模組
1、超級塊(super_block),用於儲存一個檔案系統的所有元資料,相當於這個檔案系統的資訊庫,為其他的模組提供資訊。因此一個超級塊可代表一個檔案系統。檔案系統的任意元資料修改都要修改超級塊。超級塊物件是常駐記憶體並被快取的。
2、目錄項模組,管理路徑的目錄項。比如一個路徑 /home/foo/hello.txt,那麼目錄項有home, foo, hello.txt。目錄項的塊,儲存的是這個目錄下的所有的檔案的inode號和檔名等資訊。其內部是樹形結構,作業系統檢索一個檔案,都是從根目錄開始,按層次解析路徑中的所有目錄,直到定位到檔案。
3、inode模組,管理一個具體的檔案,是檔案的唯一標識,一個檔案對應一個inode。通過inode可以方便的找到檔案在磁碟扇區的位置。同時inode模組可連結到address_space模組,方便查詢自身檔案資料是否已經快取。
4、開啟檔案列表模組,包含所有核心已經開啟的檔案。已經開啟的檔案物件由open系統呼叫在核心中建立,也叫檔案控制代碼。開啟檔案列表模組中包含一個列表,每個列表表項是一個結構體struct file,結構體中的資訊用來表示開啟的一個檔案的各種狀態引數。
5、file_operations模組。這個模組中維護一個數據結構,是一系列函式指標的集合,其中包含所有可以使用的系統呼叫函式,例如open、read、write、mmap等。每個開啟檔案(開啟檔案列表模組的一個表項)都可以連線到file_operations模組,從而對任何已開啟的檔案,通過系統呼叫函式,實現各種操作。
6、address_space模組,它表示一個檔案在頁快取中已經快取了的物理頁。它是頁快取和外部裝置中檔案系統的橋樑。如果將檔案系統可以理解成資料來源,那麼address_space可以說關聯了記憶體系統和檔案系統。我們會在文章後面繼續討論。
模組間的相互作用和邏輯關係如下圖所示:
由圖可以看出:
1、每個模組都維護了一個X_op指標指向它所對應的操作物件X_operations。
2、超級塊維護了一個s_files指標指向了“已開啟檔案列表模組”,即核心所有的開啟檔案的連結串列,這個連結串列資訊是所有程序共享的。
3、目錄操作模組和inode模組都維護了一個X_sb指標指向超級塊,從而可以獲得整個檔案系統的元資料資訊。
4、 目錄項物件和inode物件各自維護了指向對方的指標,可以找到對方的資料。
5、已開啟檔案列表上每一個file結構體例項維護了一個f_dentry指標,指向了它對應的目錄項,從而可以根據目錄項找到它對應的inode資訊。
6、已開啟檔案列表上每一個file結構體例項維護了一個f_op指標,指向可以對這個檔案進行操作的所有函式集合file_operations。
7、inode中不僅有和其他模組關聯的指標,重要的是它可以指向address_space模組,從而獲得自身檔案在記憶體中的快取資訊。
8、address_space內部維護了一個樹結構來指向所有的物理頁結構page,同時維護了一個host指標指向inode來獲得檔案的元資料。
程序和虛擬檔案系統互動
1、核心使用task_struct來表示單個程序的描述符,其中包含維護一個程序的所有資訊。task_struct結構體中維護了一個 files的指標(和“已開啟檔案列表”上的表項是不同的指標)來指向結構體files_struct,files_struct中包含檔案描述符表和開啟的檔案物件資訊。
2、file_struct中的檔案描述符表實際是一個file型別的指標列表(和“已開啟檔案列表”上的表項是相同的指標),可以支援動態擴充套件,每一個指標指向虛擬檔案系統中檔案列表模組的某一個已開啟的檔案。
3、file結構一方面可從f_dentry連結到目錄項模組以及inode模組,獲取所有和檔案相關的資訊,另一方面連結file_operations子模組,其中包含所有可以使用的系統呼叫函式,從而最終完成對檔案的操作。這樣,從程序到程序的檔案描述符表,再關聯到已開啟檔案列表上對應的檔案結構,從而呼叫其可執行的系統呼叫函式,實現對檔案的各種操作。
程序 vs 檔案列表 vs Inode
1、多個程序可以同時指向一個開啟檔案物件(檔案列表表項),例如父程序和子程序間共享檔案物件;
2、一個程序可以多次開啟一個檔案,生成不同的檔案描述符,每個檔案描述符指向不同的檔案列表表項。但是由於是同一個檔案,inode唯一,所以這些檔案列表表項都指向同一個inode。通過這樣的方法實現檔案共享(共享同一個磁碟檔案);
回到頂部I/O 緩衝區
概念
如快取記憶體(cache)產生的原理類似,在I/O過程中,讀取磁碟的速度相對記憶體讀取速度要慢的多。因此為了能夠加快處理資料的速度,需要將讀取過的資料快取在記憶體裡。而這些快取在記憶體裡的資料就是高速緩衝區(buffer cache),下面簡稱為“buffer”。
具體來說,buffer(緩衝區)是一個用於儲存速度不同步的裝置或優先順序不同的裝置之間傳輸資料的區域。一方面,通過緩衝區,可以使程序之間的相互等待變少,從而使從速度慢的裝置讀入資料時,速度快的裝置的操作程序不發生間斷。另一方面,可以保護硬碟或減少網路傳輸的次數。
Buffer和Cache
buffer和cache是兩個不同的概念:cache是快取記憶體,用於CPU和記憶體之間的緩衝;buffer是I/O快取,用於記憶體和硬碟的緩衝;簡單的說,cache是加速“讀”,而buffer是緩衝“寫”,前者解決讀的問題,儲存從磁碟上讀出的資料,後者是解決寫的問題,儲存即將要寫入到磁碟上的資料。
Buffer Cache和 Page Cache
buffer cache和page cache都是為了處理裝置和記憶體互動時高速訪問的問題。buffer cache可稱為塊緩衝器,page cache可稱為頁緩衝器。在linux不支援虛擬記憶體機制之前,還沒有頁的概念,因此緩衝區以塊為單位對裝置進行。在linux採用虛擬記憶體的機制來管理記憶體後,頁是虛擬記憶體管理的最小單位,開始採用頁緩衝的機制來緩衝記憶體。Linux2.6之後核心將這兩個快取整合,頁和塊可以相互對映,同時,頁快取page cache面向的是虛擬記憶體,塊I/O快取Buffer cache是面向塊裝置。需要強調的是,頁快取和塊快取對程序來說就是一個儲存系統,程序不需要關注底層的裝置的讀寫。
buffer cache和page cache兩者最大的區別是快取的粒度。buffer cache面向的是檔案系統的塊。而核心的記憶體管理元件採用了比檔案系統的塊更高級別的抽象:頁page,其處理的效能更高。因此和記憶體管理互動的快取元件,都使用頁快取。
回到頂部Page Cache
頁快取是面向檔案,面向記憶體的。通俗來說,它位於記憶體和檔案之間緩衝區,檔案IO操作實際上只和page cache互動,不直接和記憶體互動。page cache可以用在所有以檔案為單元的場景下,比如網路檔案系統等等。page cache通過一系列的資料結構,比如inode, address_space, struct page,實現將一個檔案對映到頁的級別:
1、struct page結構標誌一個實體記憶體頁,通過page + offset就可以將此頁幀定位到一個檔案中的具體位置。同時struct page還有以下重要引數:
(1)標誌位flags來記錄該頁是否是髒頁,是否正在被寫回等等;
(2)mapping指向了地址空間address_space,表示這個頁是一個頁快取中頁,和一個檔案的地址空間對應;
(3)index記錄這個頁在檔案中的頁偏移量;
2、檔案系統的inode實際維護了這個檔案所有的塊block的塊號,通過對檔案偏移量offset取模可以很快定位到這個偏移量所在的檔案系統的塊號,磁碟的扇區號。同樣,通過對檔案偏移量offset進行取模可以計算出偏移量所在的頁的偏移量。
3、page cache快取元件抽象了地址空間address_space這個概念來作為檔案系統和頁快取的中間橋樑。地址空間address_space通過指標可以方便的獲取檔案inode和struct page的資訊,所以可以很方便地定位到一個檔案的offset在各個元件中的位置,即通過:檔案位元組偏移量 --> 頁偏移量 --> 檔案系統塊號 block --> 磁碟扇區號
4、頁快取實際上就是採用了一個基數樹結構將一個檔案的內容組織起來存放在實體記憶體struct page中。一個檔案inode對應一個地址空間address_space。而一個address_space對應一個頁快取基數樹。它們之間的關係如下:
回到頂部
Address Space
下面我們總結已經討論過的address_space所有功能。address_space是Linux核心中的一個關鍵抽象,它被作為檔案系統和頁快取的中間介面卡,用來指示一個檔案在頁快取中已經快取了的物理頁。因此,它是頁快取和外部裝置中檔案系統的橋樑。如果將檔案系統可以理解成資料來源,那麼address_space可以說關聯了記憶體系統和檔案系統。
由圖中可以看到,地址空間address_space連結到頁快取基數樹和inode,因此address_space通過指標可以方便的獲取檔案inode和page的資訊。那麼頁快取是如何通過address_space實現緩衝區功能的?我們再來看完整的檔案讀寫流程。
回到頂部檔案讀寫基本流程
讀檔案
1、程序呼叫庫函式向核心發起讀檔案請求;
2、核心通過檢查程序的檔案描述符定位到虛擬檔案系統的已開啟檔案列表表項;
3、呼叫該檔案可用的系統呼叫函式read()
3、read()函式通過檔案表項鍊接到目錄項模組,根據傳入的檔案路徑,在目錄項模組中檢索,找到該檔案的inode;
4、在inode中,通過檔案內容偏移量計算出要讀取的頁;
5、通過inode找到檔案對應的address_space;
6、在address_space中訪問該檔案的頁快取樹,查詢對應的頁快取結點:
(1)如果頁快取命中,那麼直接返回檔案內容;
(2)如果頁快取缺失,那麼產生一個頁缺失異常,建立一個頁快取頁,同時通過inode找到檔案該頁的磁碟地址,讀取相應的頁填充該快取頁;重新進行第6步查詢頁快取;
7、檔案內容讀取成功。
寫檔案
前5步和讀檔案一致,在address_space中查詢對應頁的頁快取是否存在:
6、如果頁快取命中,直接把檔案內容修改更新在頁快取的頁中。寫檔案就結束了。這時候檔案修改位於頁快取,並沒有寫回到磁碟檔案中去。
7、如果頁快取缺失,那麼產生一個頁缺失異常,建立一個頁快取頁,同時通過inode找到檔案該頁的磁碟地址,讀取相應的頁填充該快取頁。此時快取頁命中,進行第6步。
8、一個頁快取中的頁如果被修改,那麼會被標記成髒頁。髒頁需要寫回到磁碟中的檔案塊。有兩種方式可以把髒頁寫回磁碟:
(1)手動呼叫sync()或者fsync()系統呼叫把髒頁寫回
(2)pdflush程序會定時把髒頁寫回到磁碟
同時注意,髒頁不能被置換出記憶體,如果髒頁正在被寫回,那麼會被設定寫回標記,這時候該頁就被上鎖,其他寫請求被阻塞直到鎖釋放。
轉載自:http://www.cnblogs.com/huxiao-tee/p/4657851.html
相關推薦
從核心檔案系統看檔案讀寫過程
回到頂部系統呼叫作業系統的主要功能是為管理硬體資源和為應用程式開發人員提供良好的環境,但是計算機系統的各種硬體資源是有限的,因此為了保證每一個程序都能安全的執行。處理器設有兩種模式:“使用者模式”與“核心模式”。一些容易發生安全問題的操作都被限制在只有核心模式下才可以執行,例
Hadoop分散式檔案系統——HDFS的讀寫
HDFS是執行在通用硬體平臺上的可容錯分散式檔案系統。它優化了大檔案的流式讀取模式,適用於那些高吞吐並且對延遲性要求相對比較低的場景。它還通過檔案“一次寫入,多次讀取”的簡單策略保證了資料的一致性。HDFS亦使用了“塊複製”的概念,讓資料在叢集的
node.js 使用fs模組對系統檔案及目錄進行讀寫操作
如果要用這個模組,首先需要引入,fs已經屬於node.js自帶的模組,所以直接引入即可 var fs = require('fs');1.讀取檔案readFile方法使用 fs.readFile(filename,[option],callback) 方法讀取檔案。
Python:檔案批量建立、讀寫、複製、內容修改和重新命名
需求:一個郵件檔案對應一個CSV檔案,對大量的郵件檔案重新命名,然後把此檔案移動到對應的CSV檔案目錄下,檢查CSV檔案中郵件名稱內容與格式的正確性,不正確的做出修改,正確的保留。資料夾中最後是一個(視訊、郵件等)檔案對應一個CSV檔案。 1.建立並寫入CSV檔案
Linux 檔案和目錄的讀寫執行許可權詳解
首先了解一個ls -l 檔案的每列含義 [[email protected] ~]# ls -l a -rw-r–rw- 1 test002 tester 279103 9月 2 13:21 a 下面介紹每列含義: (1)第一列:檔案型別和許可權,這部分稍後重
Java:簡單的讀寫XML檔案之使用DOM4J讀寫
Dom4J方式解析XML檔案。dom4j是非官方提供的xml檔案解析方式,因此需要去第三方下載dom4j的jar包 File file = new File("D:\\chengmuzhe\\java\\JavaOOP6.0\\students.xml"); SAXReader reader = n
java指定編碼的按行讀寫txt檔案(幾種讀寫方式的比較)
輸入輸出的幾種形式 1.FileReader,FileWriter File r = new File("temp.txt") FileReader f = new FileReader(name);//讀取檔案name BufferedReader b = new Buf
python筆記1——關於檔案的開啟與讀寫
一、檔案的開啟與關閉1.open,close函式 #-*- coding:utf-8 -*- # 1、w 寫模式,它是不能讀的,如果用w模式開啟一個已經存在的檔案,會清空以前的檔案內容,重新寫 # w+ 是讀寫內容,只要沾上w,肯定會清空原來的檔案 # 2、r 讀模式,只能讀,不能寫,而且檔案
C# 基礎(十三)C# XML配置檔案、ini配置檔案的建立、讀寫:動態修改IP
一、簡介 傳統的配置檔案ini已有被xml檔案逐步代替的趨勢,這裡主要討論XML配置檔案。 二、ini網址 https://www.cnblogs.com/cncc/p/3415694.html(重點) https://www.cnblogs.com/xmy-007/p/640022
Python中的檔案IO操作(讀寫檔案、追加檔案)
【注】:下述操作過程是結合多種網路方法,然後自己實踐的結果。寫在這裡,主要目的是加深記憶,也希望能幫助後來者 Python中檔案的讀寫包含三個步驟:開啟檔案,讀/寫檔案,關閉檔案。 檔案開啟之後必
c語言中對檔案的建立和讀寫
在c語言中,fopen用於建立檔案,fwrite用於將資料寫入檔案,而fread用於讀取檔案中的資料,fclose用於關閉檔案(在有些編輯器中如VS2017要使用fopen_s、fwrite_s和fread_s、fclose_s或者在程式碼開始前使用#pragram warn
Python 檔案操作中的讀寫模式:open(path, '-模式-',encoding='UTF-8')
open(path, ‘-模式-‘,encoding=’UTF-8’) 即open(路徑+檔名, 讀寫模式, 編碼) 在python對檔案進行讀寫操作的時候,常常涉及到“讀寫模式”,整理了一下常見的幾種模式,如下: 讀寫模式: r :只讀 r+ :
Java IO流之普通檔案流和隨機讀寫流區別
普通檔案流和隨機讀寫流區別 FileInputStream和FileOutputStream FileReader和FileWriter RandomAccessFile 兩者區別:
Hadoop之HDFS檔案讀寫過程
4.DFSOutputStream將資料分成塊,寫入data queue。data queue由Data Streamer讀取,並通知元資料節點分配資料節點,用來儲存資料塊(每塊預設複製3塊)。分配的資料節點放在一個pipeline裡。Data Streamer將資料塊寫入pipeline中的第
檔案開啟關閉與讀寫等基本操作 C++
首先在c++中想要操作檔案流,必須定義標頭檔案<fstream>而且檔案流不像標準I/O物件,所以在使用之前必須呼叫相對應的建構函式來構造建流物件。第一可以直接呼叫建構函式ifstream ifs// 定義檔案輸入物件 ofstream ofs //定義檔案輸出
file外掛的使用詳解(檔案的建立、讀寫,資料夾建立等)
Cordova 提供了一個 file 外掛,通過這個外掛我們很方便地實現在各種裝置下對檔案、和資料夾進行訪問,編輯等各種操作。 一、新增File外掛首先我們要在“終端”中進入工程所在的目錄,然後執行如下命令新增 file 外掛: 1 cordova plug
對於ARM的啟動,系統升級,燒寫過程和檔案系統等方面的總結分析
本文所述的ARM的指的是Cortex A系列以及ARM9,ARM11,跑Linux作業系統。對於CortexM系列並不一定完全適用; 談到ARM以及啟動和燒寫等方面,首先我們要明確一下幾個關鍵詞:Uboot,Cmdline,啟動方式選擇,檔案系統格式,儲存介質,如NAND,
暫存器檔案,隨機訪問儲存器讀寫理解
狀態:特徵是會保持一段時間的,不會突變。 時序(sequential ,順序)電路:預測輸出會怎麼變化,是由當前狀態和輸入決定。(時序電路的特徵和時鐘沒關係) 暫存器和時序邏輯什麼關係。!!!!! 通過控制邏輯(時鐘訊號與地址為輸入),控制儲存器是否接受時鐘訊號的控
【Java】File類檔案管理及IO讀寫、複製操作
File類的總結: 1.檔案和資料夾的建立 2.檔案的讀取 3.檔案的寫入 4.檔案的複製(字元流、位元組流、處理流) 5.以圖片地址下載圖片 檔案和資料夾 相關函式 (boolean) mkdir() 建立此抽象路徑名指定的目錄 (boolean) mkdi
C# 對文字檔案的幾種讀寫方法總結
計算機在最初只支援ASCII編碼,但是後來為了支援其他語言中的字元(比如漢字)以及一些特殊字元(比如€),就引入了Unicode字符集。基於Unicode字符集的編碼方式有很多,比如UTF-7、UTF-8、Unicode以及UTF-32。在Windows作業系統中,一個文