這篇文章主要講解了“Linux文件系統(tǒng)基本概念是什么”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“Linux文件系統(tǒng)基本概念是什么”吧!
讓客戶滿意是我們工作的目標,不斷超越客戶的期望值來自于我們對這個行業(yè)的熱愛。我們立志把好的技術通過有效、簡單的方式提供給客戶,將通過不懈努力成為客戶在信息化領域值得信任、有價值的長期合作伙伴,公司提供的服務項目有:主機域名、虛擬主機、營銷軟件、網站建設、安丘網站維護、網站推廣。
Linux 文件系統(tǒng)基本概念
Linux 在最初的設計是 MINIX1 文件系統(tǒng),它只支持 14 字節(jié)的文件名,它的最大文件只支持到 64 MB。在 MINIX 1 之后的文件系統(tǒng)是 ext 文件系統(tǒng)。ext 系統(tǒng)相較于 MINIX 1 來說,在支持字節(jié)大小和文件大小上均有很大提升,但是 ext 的速度仍沒有 MINIX 1 快,于是,ext 2 被開發(fā)出來,它能夠支持長文件名和大文件,而且具有比 MINIX 1 更好的性能。這使他成為 Linux 的主要文件系統(tǒng)。只不過 Linux 會使用 VFS 曾支持多種文件系統(tǒng)。在 Linux 鏈接時,用戶可以動態(tài)的將不同的文件系統(tǒng)掛載倒 VFS 上。
Linux 中的文件是一個任意長度的字節(jié)序列,Linux 中的文件可以包含任意信息,比如 ASCII 碼、二進制文件和其他類型的文件是不加區(qū)分的。
為了方便起見,文件可以被組織在一個目錄中,目錄存儲成文件的形式在很大程度上可以作為文件處理。目錄可以有子目錄,這樣形成有層次的文件系統(tǒng),Linux 系統(tǒng)下面的根目錄是 / ,它通常包含了多個子目錄。字符 / 還用于對目錄名進行區(qū)分,例如 「/usr/cxuan」 表示的就是根目錄下面的 usr 目錄,其中有一個叫做 cxuan 的子目錄。
下面我們介紹一下 Linux 系統(tǒng)根目錄下面的目錄名
/bin,它是重要的二進制應用程序,包含二進制文件,系統(tǒng)的所有用戶使用的命令都在這里
/boot,啟動包含引導加載程序的相關文件
/dev,包含設備文件,終端文件,USB 或者連接到系統(tǒng)的任何設備
/etc,配置文件,啟動腳本等,包含所有程序所需要的配置文件,也包含了啟動/停止單個應用程序的啟動和關閉 shell 腳本
/home,本地主要路徑,所有用戶用 home 目錄存儲個人信息
/lib,系統(tǒng)庫文件,包含支持位于 /bin 和 /sbin 下的二進制庫文件
/lost+found,在根目錄下提供一個遺失+查找系統(tǒng),必須在 root 用戶下才能查看當前目錄下的內容
/media,掛載可移動介質
/mnt,掛載文件系統(tǒng)
/opt,提供一個可選的應用程序安裝目錄
/proc,特殊的動態(tài)目錄,用于維護系統(tǒng)信息和狀態(tài),包括當前運行中進程信息
/root,root 用戶的主要目錄文件夾
/sbin,重要的二進制系統(tǒng)文件
/tmp, 系統(tǒng)和用戶創(chuàng)建的臨時文件,系統(tǒng)重啟時,這個目錄下的文件都會被刪除
/usr,包含絕大多數(shù)用戶都能訪問的應用程序和文件
/var,經常變化的文件,諸如日志文件或數(shù)據(jù)庫等
在 Linux 中,有兩種路徑,一種是 絕對路徑(absolute path) ,絕對路徑告訴你從根目錄下查找文件,絕對路徑的缺點是太長而且不太方便。還有一種是 相對路徑(relative path) ,相對路徑所在的目錄也叫做工作目錄(working directory)。
如果 /usr/local/books 是工作目錄,那么 shell 命令
cp books books-replica
就表示的是相對路徑,而
cp /usr/local/books/books /usr/local/books/books-replica
則表示的是絕對路徑。
在 Linux 中經常出現(xiàn)一個用戶使用另一個用戶的文件或者使用文件樹結構中的文件。兩個用戶共享同一個文件,這個文件位于某個用戶的目錄結構中,另一個用戶需要使用這個文件時,必須通過絕對路徑才能引用到他。如果絕對路徑很長,那么每次輸入起來會變的非常麻煩,所以 Linux 提供了一種 鏈接(link) 機制。
舉個例子,下面是一個使用鏈接之前的圖
以上所示,比如有兩個工作賬戶 jianshe 和 cxuan,jianshe 想要使用 cxuan 賬戶下的 A 目錄,那么它可能會輸入 /usr/cxuan/A ,這是一種未使用鏈接之后的圖。
使用鏈接后的示意如下
現(xiàn)在,jianshe 可以創(chuàng)建一個鏈接來使用 cxuan 下面的目錄了。‘
當一個目錄被創(chuàng)建出來后,有兩個目錄項也同時被創(chuàng)建出來,它們就是 . 和 .. ,前者代表工作目錄自身,后者代表該目錄的父目錄,也就是該目錄所在的目錄。這樣一來,在 /usr/jianshe 中訪問 cxuan 中的目錄就是 ../cxuan/xxxLinux 文件系統(tǒng)不區(qū)分磁盤的,這是什么意思呢?一般來說,一個磁盤中的文件系統(tǒng)相互之間保持獨立,如果一個文件系統(tǒng)目錄想要訪問另一個磁盤中的文件系統(tǒng),在 Windows 中你可以像下面這樣。
兩個文件系統(tǒng)分別在不同的磁盤中,彼此保持獨立。
而在 Linux 中,是支持掛載的,它允許一個磁盤掛在到另外一個磁盤上,那么上面的關系會變成下面這樣
掛在之后,兩個文件系統(tǒng)就不再需要關心文件系統(tǒng)在哪個磁盤上了,兩個文件系統(tǒng)彼此可見。
Linux 文件系統(tǒng)的另外一個特性是支持 加鎖(locking)。在一些應用中會出現(xiàn)兩個或者更多的進程同時使用同一個文件的情況,這樣很可能會導致競爭條件(race condition)。一種解決方法是對其進行加不同粒度的鎖,就是為了防止某一個進程只修改某一行記錄從而導致整個文件都不能使用的情況。
POSIX 提供了一種靈活的、不同粒度級別的鎖機制,允許一個進程使用一個不可分割的操作對一個字節(jié)或者整個文件進行加鎖。加鎖機制要求嘗試加鎖的進程指定其「要加鎖的文件,開始位置以及要加鎖的字節(jié)」
Linux 系統(tǒng)提供了兩種鎖:「共享鎖和互斥鎖」。如果文件的一部分已經加上了共享鎖,那么再加排他鎖是不會成功的;如果文件系統(tǒng)的一部分已經被加了互斥鎖,那么在互斥鎖解除之前的任何加鎖都不會成功。為了成功加鎖、請求加鎖的部分的所有字節(jié)都必須是可用的。
在加鎖階段,進程需要設計好加鎖失敗后的情況,也就是判斷加鎖失敗后是否選擇阻塞,如果選擇阻塞式,那么當已經加鎖的進程中的鎖被刪除時,這個進程會解除阻塞并替換鎖。如果進程選擇非阻塞式的,那么就不會替換這個鎖,會立刻從系統(tǒng)調用中返回,標記狀態(tài)碼表示是否加鎖成功,然后進程會選擇下一個時間再次嘗試。
加鎖區(qū)域是可以重疊的。下面我們演示了三種不同條件的加鎖區(qū)域。
如上圖所示,A 的共享鎖在第四字節(jié)到第八字節(jié)進行加鎖
如上圖所示,進程在 A 和 B 上同時加了共享鎖,其中 6 - 8 字節(jié)是重疊鎖
如上圖所示,進程 A 和 B 和 C 同時加了共享鎖,那么第六字節(jié)和第七字節(jié)是共享鎖。
如果此時一個進程嘗試在第 6 個字節(jié)處加鎖,此時會設置失敗并阻塞,由于該區(qū)域被 A B C 同時加鎖,那么只有等到 A B C 都釋放鎖后,進程才能加鎖成功。
Linux 文件系統(tǒng)調用
許多系統(tǒng)調用都會和文件與文件系統(tǒng)有關。我們首先先看一下對單個文件的系統(tǒng)調用,然后再來看一下對整個目錄和文件的系統(tǒng)調用。
為了創(chuàng)建一個新的文件,會使用到 creat 方法,注意沒有 e。
?這里說一個小插曲,曾經有人問 UNIX 創(chuàng)始人 Ken Thompson,如果有機會重新寫 UNIX ,你會怎么辦,他回答自己要把 creat 改成 create ,哈哈哈哈。?
這個系統(tǒng)調用的兩個參數(shù)是文件名和保護模式
fd = creat("aaa",mode);
這段命令會創(chuàng)建一個名為 aaa 的文件,并根據(jù) mode 設置文件的保護位。這些位決定了哪個用戶可能訪問文件、如何訪問。
creat 系統(tǒng)調用不僅僅創(chuàng)建了一個名為 aaa 的文件,還會打開這個文件。為了允許后續(xù)的系統(tǒng)調用訪問這個文件,這個 creat 系統(tǒng)調用會返回一個 非負整數(shù), 這個就叫做 文件描述符(file descriptor),也就是上面的 fd。
如果在已經存在的文件上調用了 creat 系統(tǒng)調用,那么該文件中的內容會被清除,從 0 開始。通過設置合適的參數(shù),open 系統(tǒng)調用也能夠創(chuàng)建文件。
下面讓我們看一看主要的系統(tǒng)調用,如下表所示
系統(tǒng)調用 | 描述 |
---|---|
fd = creat(name,mode) | 一種創(chuàng)建一個新文件的方式 |
fd = open(file, ...) | 打開文件讀、寫或者讀寫 |
s = close(fd) | 關閉一個打開的文件 |
n = read(fd, buffer, nbytes) | 從文件中向緩存中讀入數(shù)據(jù) |
n = write(fd, buffer, nbytes) | 從緩存中向文件中寫入數(shù)據(jù) |
position = lseek(fd, offset, whence) | 移動文件指針 |
s = stat(name, &buf) | 獲取文件信息 |
s = fstat(fd, &buf) | 獲取文件信息 |
s = pipe(&fd[0]) | 創(chuàng)建一個管道 |
s = fcntl(fd,...) | 文件加鎖等其他操作 |
為了對一個文件進行讀寫的前提是先需要打開文件,必須使用 creat 或者 open 打開,參數(shù)是打開文件的方式,是只讀、可讀寫還是只寫。open 系統(tǒng)調用也會返回文件描述符。打開文件后,需要使用 close 系統(tǒng)調用進行關閉。close 和 open 返回的 fd 總是未被使用的最小數(shù)量。
?什么是文件描述符?文件描述符就是一個數(shù)字,這個數(shù)字標示了計算機操作系統(tǒng)中打開的文件。它描述了數(shù)據(jù)資源,以及訪問資源的方式。?
當程序要求打開一個文件時,內核會進行如下操作
授予訪問權限
在全局文件表(global file table)中創(chuàng)建一個條目(entry)向軟件提供條目的位置
文件描述符由唯一的非負整數(shù)組成,系統(tǒng)上每個打開的文件至少存在一個文件描述符。文件描述符最初在 Unix 中使用,并且被包括 Linux,macOS 和 BSD 在內的現(xiàn)代操作系統(tǒng)所使用。
當一個進程成功訪問一個打開的文件時,內核會返回一個文件描述符,這個文件描述符指向全局文件表的 entry 項。這個文件表項包含文件的 inode 信息,字節(jié)位移,訪問限制等。例如下圖所示
默認情況下,前三個文件描述符為 STDIN(標準輸入)、STDOUT(標準輸出)、STDERR(標準錯誤)。
標準輸入的文件描述符是 0 ,在終端中,默認為用戶的鍵盤輸入
標準輸出的文件描述符是 1 ,在終端中,默認為用戶的屏幕
與錯誤有關的默認數(shù)據(jù)流是 2,在終端中,默認為用戶的屏幕。
在簡單聊了一下文件描述符后,我們繼續(xù)回到文件系統(tǒng)調用的探討。
在文件系統(tǒng)調用中,開銷最大的就是 read 和 write 了。read 和 write 都有三個參數(shù)
文件描述符:告訴需要對哪一個打開文件進行讀取和寫入
緩沖區(qū)地址:告訴數(shù)據(jù)需要從哪里讀取和寫入哪里
統(tǒng)計:告訴需要傳輸多少字節(jié)
這就是所有的參數(shù)了,這個設計非常簡單輕巧。
雖然幾乎所有程序都按順序讀取和寫入文件,但是某些程序需要能夠隨機訪問文件的任何部分。與每個文件相關聯(lián)的是一個指針,該指針指示文件中的當前位置。順序讀取(或寫入)時,它通常指向要讀取(寫入)的下一個字節(jié)。如果指針在讀取 1024 個字節(jié)之前位于 4096 的位置,則它將在成功讀取系統(tǒng)調用后自動移至 5120 的位置。
Lseek 系統(tǒng)調用會更改指針位置的值,以便后續(xù)對 read 或 write 的調用可以在文件中的任何位置開始,甚至可以超出文件末尾。
?lseek = Lseek ,段首大寫。?
lseek 避免叫做 seek 的原因就是 seek 已經在之前 16 位的計算機上用于搜素功能了。
Lseek 有三個參數(shù):第一個是文件的文件描述符,第二個是文件的位置;第三個告訴文件位置是相對于文件的開頭,當前位置還是文件的結尾
lseek(int fildes, off_t offset, int whence);
lseek 的返回值是更改文件指針后文件中的絕對位置。lseek 是唯一從來不會造成真正磁盤查找的系統(tǒng)調用,它只是更新當前的文件位置,這個文件位置就是內存中的數(shù)字。
對于每個文件,Linux 都會跟蹤文件模式(常規(guī),目錄,特殊文件),大小,最后修改時間以及其他信息。程序能夠通過 stat 系統(tǒng)調用看到這些信息。第一個參數(shù)就是文件名,第二個是指向要放置請求信息結構的指針。這些結構的屬性如下圖所示。
存儲文件的設備 |
---|
存儲文件的設備 |
i-node 編號 |
文件模式(包括保護位信息) |
文件鏈接的數(shù)量 |
文件所有者標識 |
文件所屬的組 |
文件大小(字節(jié)) |
創(chuàng)建時間 |
最后一個修改/訪問時間 |
fstat 調用和 stat 相同,只有一點區(qū)別,fstat 可以對打開文件進行操作,而 stat 只能對路徑進行操作。
pipe 文件系統(tǒng)調用被用來創(chuàng)建 shell 管道。它會創(chuàng)建一系列的偽文件,來緩沖和管道組件之間的數(shù)據(jù),并且返回讀取或者寫入緩沖區(qū)的文件描述符。在管道中,像是如下操作
sortsort 進程將會輸出到文件描述符1,也就是標準輸出,寫入管道中,而 head 進程將從管道中讀入。在這種方式中,sort 只是從文件描述符 0 中讀取并寫入到文件描述符 1 (管道)中,甚至不知道它們已經被重定向了。如果沒有重定向的話,sort 會自動的從鍵盤讀入并輸出到屏幕中。
最后一個系統(tǒng)調用是 fcntl,它用來鎖定和解鎖文件,應用共享鎖和互斥鎖,或者是執(zhí)行一些文件相關的其他操作。
現(xiàn)在我們來關心一下和整體目錄和文件系統(tǒng)相關的系統(tǒng)調用,而不是把精力放在單個的文件上,下面列出了這些系統(tǒng)調用,我們一起來看一下。
系統(tǒng)調用 描述 s = mkdir(path,mode) 創(chuàng)建一個新的目錄 s = rmdir(path) 移除一個目錄 s = link(oldpath,newpath) 創(chuàng)建指向已有文件的鏈接 s = unlink(path) 取消文件的鏈接 s = chdir(path) 改變工作目錄 dir = opendir(path) 打開一個目錄讀取 s = closedir(dir) 關閉一個目錄 dirent = readdir(dir) 讀取一個目錄項 rewinddir(dir) 回轉目錄使其在此使用 可以使用 mkdir 和 rmdir 創(chuàng)建和刪除目錄。但是需要注意,只有目錄為空時才可以刪除。
創(chuàng)建一個指向已有文件的鏈接時會創(chuàng)建一個目錄項(directory entry)。系統(tǒng)調用 link 來創(chuàng)建鏈接,oldpath 代表已有的路徑,newpath 代表需要鏈接的路徑,使用 unlink 可以刪除目錄項。當文件的最后一個鏈接被刪除時,這個文件會被自動刪除。
使用 chdir 系統(tǒng)調用可以改變工作目錄。
最后四個系統(tǒng)調用是用于讀取目錄的。和普通文件類似,他們可以被打開、關閉和讀取。每次調用 readdir 都會以固定的格式返回一個目錄項。用戶不能對目錄執(zhí)行寫操作,但是可以使用 creat 或者 link 在文件夾中創(chuàng)建一個目錄,或使用 unlink 刪除一個目錄。用戶不能在目錄中查找某個特定文件,但是可以使用 rewindir 作用于一個打開的目錄,使他能在此從頭開始讀取。
Linux 文件系統(tǒng)的實現(xiàn)
下面我們主要討論一下 虛擬文件系統(tǒng)(Virtual File System)。VFS 對高層進程和應用程序隱藏了 Linux 支持的所有文件系統(tǒng)的區(qū)別,以及文件系統(tǒng)是存儲在本地設備,還是需要通過網絡訪問遠程設備。設備和其他特殊文件和 VFS 層相關聯(lián)。接下來,我們就會探討一下第一個 Linux 廣泛傳播的文件系統(tǒng):ext2。隨后,我們就會探討 ext4 文件系統(tǒng)所做的改進。各種各樣的其他文件系統(tǒng)也正在使用中。所有 Linux 系統(tǒng)都可以處理多個磁盤分區(qū),每個磁盤分區(qū)上都有不同的文件系統(tǒng)。
Linux 虛擬文件系統(tǒng)
為了能夠使應用程序能夠在不同類型的本地或者遠程設備上的文件系統(tǒng)進行交互,因為在 Linux 當中文件系統(tǒng)千奇百種,比較常見的有 EXT3、EXT4,還有基于內存的 ramfs、tmpfs 和基于網絡的 nfs,和基于用戶態(tài)的 fuse,當然 fuse 應該不能完全的文件系統(tǒng),只能算是一個能把文件系統(tǒng)實現(xiàn)放到用戶態(tài)的模塊,滿足了內核文件系統(tǒng)的接口,他們都是文件系統(tǒng)的一種實現(xiàn)。對于這些文件系統(tǒng),Linux 做了一層抽象就是 VFS虛擬文件系統(tǒng),
下表總結了 VFS 支持的四個主要的文件系統(tǒng)結構。
對象 描述 超級塊 特定的文件系統(tǒng) Dentry 目錄項,路徑的一個組成部分 I-node 特定的文件 File 跟一個進程相關聯(lián)的打開文件 超級塊(superblock) 包含了有關文件系統(tǒng)布局的重要信息,超級塊如果遭到破壞那么就會導致整個文件系統(tǒng)不可讀。
i-node 索引節(jié)點,包含了每一個文件的描述符。
?在 Linux 中,目錄和設備也表示為文件,因為它們具有對應的 i-node?
超級塊和索引塊所在的文件系統(tǒng)都在磁盤上有對應的結構。
為了便于某些目錄操作和路徑遍歷,比如 /usr/local/cxuan,VFS 支持一個 dentry 數(shù)據(jù)結構,該數(shù)據(jù)結構代表著目錄項。這個 dentry 數(shù)據(jù)結構有很多東西(http://books.gigatux.nl/mirror/kerneldevelopment/0672327201/ch22lev1sec7.html)這個數(shù)據(jù)結構由文件系統(tǒng)動態(tài)創(chuàng)建。
目錄項被緩存在 dentry_cache 緩存中。例如,緩存條目會緩存 /usr 、 /usr/local 等條目。如果多個進程通過硬連接訪問相同的文件,他們的文件對象將指向此緩存中的相同條目。
最后,文件數(shù)據(jù)結構是代表著打開的文件,也代表著內存表示,它根據(jù) open 系統(tǒng)調用創(chuàng)建。它支持 「read、write、sendfile、lock」 和其他在我們之前描述的系統(tǒng)調用中。
在 VFS 下實現(xiàn)的實際文件系統(tǒng)不需要在內部使用完全相同的抽象和操作。但是,它們必須在語義上實現(xiàn)與 VFS 對象指定的文件系統(tǒng)操作相同的文件系統(tǒng)操作。四個 VFS 對象中每個對象的操作數(shù)據(jù)結構的元素都是指向基礎文件系統(tǒng)中功能的指針。
Linux Ext2 文件系統(tǒng)
現(xiàn)在我們一起看一下 Linux 中最流行的一個磁盤文件系統(tǒng),那就是 ext2 。Linux 的第一個版本用于 MINIX1 文件系統(tǒng),它的文件名大小被限制為最大 64 MB。MINIX 1 文件系統(tǒng)被永遠的被它的擴展系統(tǒng) ext 取代,因為 ext 允許更長的文件名和文件大小。由于 ext 的性能低下,ext 被其替代者 ext2 取代,ext2 目前仍在廣泛使用。
一個 ext2 Linux 磁盤分區(qū)包含了一個文件系統(tǒng),這個文件系統(tǒng)的布局如下所示
Boot 塊也就是第 0 塊不是讓 Linux 使用的,而是用來加載和引導計算機啟動代碼的。在塊 0 之后,磁盤分區(qū)被分成多個組,這些組與磁盤柱面邊界所處的位置無關。
第一個塊是 超級塊(superblock)。它包含有關文件系統(tǒng)布局的信息,包括 i-node、磁盤塊數(shù)量和以及空閑磁盤塊列表的開始。下一個是 組描述符(group descriptor),其中包含有關位圖的位置,組中空閑塊和 i-node 的數(shù)量以及組中的目錄數(shù)量的信息。這些信息很重要,因為 ext2 會在磁盤上均勻分布目錄。
圖中的兩個位圖用來記錄空閑塊和空閑 i-node,這是從 MINIX 1文件系統(tǒng)繼承的選擇,大多數(shù) UNIX 文件系統(tǒng)使用位圖而不是空閑列表。每個位圖的大小是一個塊。如果一個塊的大小是 1 KB,那么就限制了塊組的數(shù)量是 8192 個塊和 8192 個 i-node。塊的大小是一個嚴格的限制,塊組的數(shù)量不固定,在 4KB 的塊中,塊組的數(shù)量增大四倍。
在超級塊之后分布的是 i-node 它們自己,i-node 取值范圍是 1 - 某些最大值。每個 i-node 是 128 字節(jié)的 long ,這些字節(jié)恰好能夠描述一個文件。i-node 包含了統(tǒng)計信息(包含了 stat 系統(tǒng)調用能獲得的所有者信息,實際上 stat 就是從 i-node 中讀取信息的),以及足夠的信息來查找保存文件數(shù)據(jù)的所有磁盤塊。
在 i-node 之后的是 數(shù)據(jù)塊(data blocks)。所有的文件和目錄都保存在這。如果一個文件或者目錄包含多個塊,那么這些塊在磁盤中的分布不一定是連續(xù)的,也有可能不連續(xù)。事實上,大文件塊可能會被拆分成很多小塊散布在整個磁盤上。
對應于目錄的 i-node 分散在整個磁盤組上。如果有足夠的空間,ext2 會把普通文件組織到與父目錄相同的塊組中,而把同一塊上的數(shù)據(jù)文件組織成初始 i-node 節(jié)點。位圖用來快速確定新文件系統(tǒng)數(shù)據(jù)的分配位置。在分配新的文件塊時,ext2 也會給該文件預分配許多額外的數(shù)據(jù)塊,這樣可以減少將來向文件寫入數(shù)據(jù)時產生的文件碎片。這種策略在整個磁盤上實現(xiàn)了文件系統(tǒng)的 負載,后續(xù)還有對文件碎片的排列和整理,而且性能也比較好。
為了達到訪問的目的,需要首先使用 Linux 系統(tǒng)調用,例如 open,這個系統(tǒng)調用會確定打開文件的路徑。路徑分為兩種,相對路徑 和 絕對路徑。如果使用相對路徑,那么就會從當前目錄開始查找,否則就會從根目錄進行查找。
目錄文件的文件名最高不能超過 255 個字符,它的分配如下圖所示
每一個目錄都由整數(shù)個磁盤塊組成,這樣目錄就可以整體的寫入磁盤。在一個目錄中,文件和子目錄的目錄項都是未經排序的,并且一個挨著一個。目錄項不能跨越磁盤塊,所以通常在每個磁盤塊的尾部會有部分未使用的字節(jié)。
上圖中每個目錄項都由四個固定長度的屬性和一個長度可變的屬性組成。第一個屬性是 i-node 節(jié)點數(shù)量,文件 first 的 i-node 編號是 19 ,文件 second 的編號是 42,目錄 third 的 i-node 編號是 88。緊隨其后的是 rec_len域,表明目錄項大小是多少字節(jié),名稱后面會有一些擴展,當名字以未知長度填充時,這個域被用來尋找下一個目錄項,直至最后的未使用。這也是圖中箭頭的含義。緊隨其后的是 類型域:F 表示的是文件,D 表示的是目錄,最后是固定長度的文件名,上面的文件名的長度依次是 5、6、5,最后以文件名結束。
rec_len 域是如何擴展的呢?如下圖所示
我們可以看到,中間的 second 被移除了,所以將其所在的域變?yōu)榈谝粋€目錄項的填充。當然,這個填充可以作為后續(xù)的目錄項。
由于目錄是按照線性的順序進行查找的,因此可能需要很長時間才能在大文件末尾找到目錄項。因此,系統(tǒng)會為近期的訪問目錄維護一個緩存。這個緩存用文件名來查找,如果緩存命中,那么就會避免線程搜索這樣昂貴的開銷。組成路徑的每個部分都在目錄緩存中保存一個 dentry 對象,并且通過 i-node 找到后續(xù)的路徑元素的目錄項,直到找到真正的文件 i - node。
比如說要使用絕對路徑來尋找一個文件,我們暫定這個路徑是 /usr/local/file,那么需要經過如下幾個步驟:
首先,系統(tǒng)會確定根目錄,它通常使用 2 號 i -node ,也就是索引 2 節(jié)點,因為索引節(jié)點 1 是 ext2 /3/4 文件系統(tǒng)上的壞塊索引節(jié)點。系統(tǒng)會將一項放在 dentry 緩存中,以應對將來對根目錄的查找。
然后,在根目錄中查找字符串 usr,得到 /usr 目錄的 i - node 節(jié)點號。/usr 的 i - node 同樣也進入 dentry 緩存。然后節(jié)點被取出,并從中解析出磁盤塊,這樣就可以讀取 /usr 目錄并查找字符串 local 了。一旦找到這個目錄項,目錄 /usr/local 的 i - node 節(jié)點就可以從中獲得。有了 /usr/local 的 i - node 節(jié)點號,就可以讀取 i - node 并確定目錄所在的磁盤塊。最后,從 /usr/local 目錄查找 file 并確定其 i - node 節(jié)點呢號。
如果文件存在,那么系統(tǒng)會提取 i - node 節(jié)點號并把它作為索引在 i - node 節(jié)點表中定位相應的 i - node 節(jié)點并裝入內存。i - node 被存放在 i - node 節(jié)點表(i-node table) 中,節(jié)點表是一個內核數(shù)據(jù)結構,它會持有當前打開文件和目錄的 i - node 節(jié)點號。下面是一些 Linux 文件系統(tǒng)支持的 i - node 數(shù)據(jù)結構。
屬性 字節(jié) 描述 Mode 2 文件屬性、保護位、setuid 和 setgid 位 Nlinks 2 指向 i - node 節(jié)點目錄項的數(shù)目 Uid 2 文件所有者的 UID Gid 2 文件所有者的 GID Size 4 文件字節(jié)大小 Addr 60 12 個磁盤塊以及后面 3 個間接塊的地址 Gen 1 每次重復使用 i - node 時增加的代號 Atime 4 最近訪問文件的時間 Mtime 4 最近修改文件的時間 Ctime 4 最近更改 i - node 的時間 現(xiàn)在我們來一起探討一下文件讀取過程,還記得 read 函數(shù)是如何調用的嗎?
n = read(fd,buffer,nbytes);當內核接管后,它會從這三個參數(shù)以及內部表與用戶有關的信息開始。內部表的其中一項是文件描述符數(shù)組。文件描述符數(shù)組用文件描述符 作為索引并為每一個打開文件保存一個表項。
文件是和 i - node 節(jié)點號相關的。那么如何通過一個文件描述符找到文件對應的 i - node 節(jié)點呢?
這里使用的一種設計思想是在文件描述符表和 i - node 節(jié)點表之間插入一個新的表,叫做 打開文件描述符(open-file-description table)。文件的讀寫位置會在打開文件描述符表中存在,如下圖所示
我們使用 shell 、P1 和 P2 來描述一下父進程、子進程、子進程的關系。Shell 首先生成 P1,P1 的數(shù)據(jù)結構就是 Shell 的一個副本,因此兩者都指向相同的打開文件描述符的表項。當 P1 運行完成后,Shell 的文件描述符仍會指向 P1 文件位置的打開文件描述。然后 Shell 生成了 P2,新的子進程自動繼承文件的讀寫位置,甚至 P2 和 Shell 都不知道文件具體的讀寫位置。
上面描述的是父進程和子進程這兩個 相關 進程,如果是一個不相關進程打開文件時,它將得到自己的打開文件描述符表項,以及自己的文件讀寫位置,這是我們需要的。
?因此,打開文件描述符相當于是給相關進程提供同一個讀寫位置,而給不相關進程提供各自私有的位置。?
i - node 包含三個間接塊的磁盤地址,它們每個指向磁盤塊的地址所能夠存儲的大小不一樣。
Linux Ext4 文件系統(tǒng)
為了防止由于系統(tǒng)崩潰和電源故障造成的數(shù)據(jù)丟失,ext2 系統(tǒng)必須在每個數(shù)據(jù)塊創(chuàng)建之后立即將其寫入到磁盤上,磁盤磁頭尋道操作導致的延遲是無法讓人忍受的。為了增強文件系統(tǒng)的健壯性,Linux 依靠日志文件系統(tǒng),ext3 是一個日志文件系統(tǒng),它在 ext2 文件系統(tǒng)的基礎之上做了改進,ext4 也是 ext3 的改進,ext4 也是一個日志文件系統(tǒng)。ext4 改變了 ext3 的塊尋址方案,從而支持更大的文件和更大的文件系統(tǒng)大小。下面我們就來描述一下 ext4 文件系統(tǒng)的特性。
具有記錄的文件系統(tǒng)最基本的功能就是記錄日志,這個日志記錄了按照順序描述所有文件系統(tǒng)的操作。通過順序寫出文件系統(tǒng)數(shù)據(jù)或元數(shù)據(jù)的更改,操作不受磁盤訪問期間磁盤頭移動的開銷。最終,這個變更會寫入并提交到合適的磁盤位置上。如果這個變更在提交到磁盤前文件系統(tǒng)宕機了,那么在重啟期間,系統(tǒng)會檢測到文件系統(tǒng)未正確卸載,那么就會遍歷日志并應用日志的記錄來對文件系統(tǒng)進行更改。
Ext4 文件系統(tǒng)被設計用來高度匹配 ext2 和 ext3 文件系統(tǒng)的,盡管 ext4 文件系統(tǒng)在內核數(shù)據(jù)結構和磁盤布局上都做了變更。盡管如此,一個文件系統(tǒng)能夠從 ext2 文件系統(tǒng)上卸載后成功的掛載到 ext4 文件系統(tǒng)上,并提供合適的日志記錄。
日志是作為循環(huán)緩沖區(qū)管理的文件。日志可以存儲在與主文件系統(tǒng)相同或者不同的設備上。日志記錄的讀寫操作會由單獨的 JBD(Journaling Block Device) 來扮演。
JBD 中有三個主要的數(shù)據(jù)結構,分別是 「log record(日志記錄)、原子操作和事務」。一個日志記錄描述了一個低級別的文件系統(tǒng)操作,這個操作通常導致塊內的變化。因為像是 write 這種系統(tǒng)調用會包含多個地方的改動 --- i - node 節(jié)點,現(xiàn)有的文件塊,新的文件塊和空閑列表等。相關的日志記錄會以原子性的方式分組。ext4 會通知系統(tǒng)調用進程的開始和結束,以此使 JBD 能夠確保原子操作的記錄都能被應用,或者一個也不被應用。最后,主要從效率方面考慮,JBD 會視原子操作的集合為事務。一個事務中的日志記錄是連續(xù)存儲的。只有在所有的變更一起應用到磁盤后,日志記錄才能夠被丟棄。
由于為每個磁盤寫出日志的開銷會很大,所以 ext4 可以配置為保留所有磁盤更改的日志,或者僅僅保留與文件系統(tǒng)元數(shù)據(jù)相關的日志更改。僅僅記錄元數(shù)據(jù)可以減少系統(tǒng)開銷,提升性能,但不能保證不會損壞文件數(shù)據(jù)。其他的幾個日志系統(tǒng)維護著一系列元數(shù)據(jù)操作的日志,例如 SGI 的 XFS。
/proc 文件系統(tǒng)
另外一個 Linux 文件系統(tǒng)是 /proc (process) 文件系統(tǒng)
?它的主要思想來源于貝爾實驗室開發(fā)的第 8 版的 UNIX,后來被 BSD 和 System V 采用。?
然而,Linux 在一些方面上對這個想法進行了擴充。它的基本概念是為系統(tǒng)中的每個進程在 /proc 中創(chuàng)建一個目錄。目錄的名字就是進程 PID,以十進制數(shù)進行表示。例如,/proc/1024 就是一個進程號為 1024 的目錄。在該目錄下是進程信息相關的文件,比如進程的命令行、環(huán)境變量和信號掩碼等。事實上,這些文件在磁盤上并不存在磁盤中。當需要這些信息的時候,系統(tǒng)會按需從進程中讀取,并以標準格式返回給用戶。
許多 Linux 擴展與 /proc 中的其他文件和目錄有關。它們包含各種各樣的關于 CPU、磁盤分區(qū)、設備、中斷向量、內核計數(shù)器、文件系統(tǒng)、已加載模塊等信息。非特權用戶可以讀取很多這樣的信息,于是就可以通過一種安全的方式了解系統(tǒng)情況。
NFS 網絡文件系統(tǒng)
從一開始,網絡就在 Linux 中扮演了很重要的作用。下面我們會探討一下 NFS(Network File System) 網絡文件系統(tǒng),它在現(xiàn)代 Linux 操作系統(tǒng)的作用是將不同計算機上的不同文件系統(tǒng)鏈接成一個邏輯整體。
NFS 架構NFS 最基本的思想是允許任意選定的一些客戶端和服務器共享一個公共文件系統(tǒng)。在許多情況下,所有的客戶端和服務器都會在同一個 LAN(Local Area Network) 局域網內共享,但是這并不是必須的。也可能是下面這樣的情況:如果客戶端和服務器距離較遠,那么它們也可以在廣域網上運行??蛻舳丝梢允欠掌?,服務器可以是客戶端,但是為了簡單起見,我們說的客戶端就是消費服務,而服務器就是提供服務的角度來聊。
每一個 NFS 服務都會導出一個或者多個目錄供遠程客戶端訪問。當一個目錄可用時,它的所有子目錄也可用。因此,通常整個目錄樹都會作為一個整體導出。服務器導出的目錄列表會用一個文件來維護,這個文件是 /etc/exports,當服務器啟動后,這些目錄可以自動的被導出??蛻舳送ㄟ^掛載這些導出的目錄來訪問它們。當一個客戶端掛載了一個遠程目錄,這個目錄就成為客戶端目錄層次的一部分,如下圖所示。
在這個示例中,一號客戶機掛載到服務器的 bin 目錄下,因此它現(xiàn)在可以使用 shell 訪問 /bin/cat 或者其他任何一個目錄。同樣,客戶機 1 也可以掛載到 二號服務器上從而訪問 /usr/local/projects/proj1 或者其他目錄。二號客戶機同樣可以掛載到二號服務器上,訪問路徑是 /mnt/projects/proj2。
從上面可以看到,由于不同的客戶端將文件掛載到各自目錄樹的不同位置,同一個文件在不同的客戶端有不同的訪問路徑和不同的名字。掛載點一般通常在客戶端本地,服務器不知道任何一個掛載點的存在。
NFS 協(xié)議
由于 NFS 的協(xié)議之一是支持 異構 系統(tǒng),客戶端和服務器可能在不同的硬件上運行不同的操作系統(tǒng),因此有必要在服務器和客戶端之間進行接口定義。這樣才能讓任何寫一個新客戶端能夠和現(xiàn)有的服務器一起正常工作,反之亦然。
NFS 就通過定義兩個客戶端 - 服務器協(xié)議從而實現(xiàn)了這個目標。協(xié)議就是客戶端發(fā)送給服務器的一連串的請求,以及服務器發(fā)送回客戶端的相應答復。
第一個 NFS 協(xié)議是處理掛載??蛻舳丝梢韵蚍掌靼l(fā)送路徑名并且請求服務器是否能夠將服務器的目錄掛載到自己目錄層次上。因為服務器不關心掛載到哪里,因此請求不會包含掛載地址。如果路徑名是合法的并且指定的目錄已經被導出,那么服務器會將文件 句柄 返回給客戶端。
?文件句柄包含唯一標識文件系統(tǒng)類型,磁盤,目錄的i節(jié)點號和安全性信息的字段。?
隨后調用讀取和寫入已安裝目錄或其任何子目錄中的文件,都將使用文件句柄。
當 Linux 啟動時會在多用戶之前運行 shell 腳本 /etc/rc ??梢詫燧d遠程文件系統(tǒng)的命令寫入該腳本中,這樣就可以在允許用戶登陸之前自動掛載必要的遠程文件系統(tǒng)。大部分 Linux 版本是支持自動掛載的。這個特性會支持將遠程目錄和本地目錄進行關聯(lián)。
相對于手動掛載到 /etc/rc 目錄下,自動掛載具有以下優(yōu)勢
如果列出的 /etc/rc 目錄下出現(xiàn)了某種故障,那么客戶端將無法啟動,或者啟動會很困難、延遲或者伴隨一些出錯信息,如果客戶根本不需要這個服務器,那么手動做了這些工作就白費了。
允許客戶端并行的嘗試一組服務器,可以實現(xiàn)一定程度的容錯率,并且性能也可以得到提高。
另一方面,我們默認在自動掛載時所有可選的文件系統(tǒng)都是相同的。由于 NFS 不提供對文件或目錄復制的支持,用戶需要自己確保這些所有的文件系統(tǒng)都是相同的。因此,大部分的自動掛載都只應用于二進制文件和很少改動的只讀的文件系統(tǒng)。
第二個 NFS 協(xié)議是為文件和目錄的訪問而設計的??蛻舳四軌蛲ㄟ^向服務器發(fā)送消息來操作目錄和讀寫文件??蛻舳艘部梢栽L問文件屬性,比如文件模式、大小、上次修改時間。NFS 支持大多數(shù)的 Linux 系統(tǒng)調用,但是 open 和 close 系統(tǒng)調用卻不支持。
?不支持 open 和 close 并不是一種疏忽,而是一種刻意的設計,完全沒有必要在讀一個文件之前對其進行打開,也沒有必要在讀完時對其進行關閉。?
NFS 使用了標準的 UNIX 保護機制,使用 rwx 位來標示所有者(owner)、組(groups)、其他用戶 。最初,每個請求消息都會攜帶調用者的 groupId 和 userId,NFS 會對其進行驗證。事實上,它會信任客戶端不會發(fā)生欺騙行為。可以使用公鑰密碼來創(chuàng)建一個安全密鑰,在每次請求和應答中使用它驗證客戶端和服務器。
NFS 實現(xiàn)
即使客戶端和服務器的代碼實現(xiàn)是獨立于 NFS 協(xié)議的,大部分的 Linux 系統(tǒng)會使用一個下圖的三層實現(xiàn),頂層是系統(tǒng)調用層,系統(tǒng)調用層能夠處理 open 、 read 、 close 這類的系統(tǒng)調用。在解析和參數(shù)檢查結束后調用第二層,虛擬文件系統(tǒng) (VFS) 層。
VFS 層的任務是維護一個表,每個已經打開的文件都在表中有一個表項。VFS 層為每一個打開的文件維護著一個虛擬i節(jié)點,簡稱為 v - node。v 節(jié)點用來說明文件是本地文件還是遠程文件。如果是遠程文件的話,那么 v - node 會提供足夠的信息使客戶端能夠訪問它們。對于本地文件,會記錄其所在的文件系統(tǒng)和文件的 i-node ,因為現(xiàn)代操作系統(tǒng)能夠支持多文件系統(tǒng)。雖然 VFS 是為了支持 NFS 而設計的,但是現(xiàn)代操作系統(tǒng)都會使用 VFS,而不管有沒有 NFS。
感謝各位的閱讀,以上就是“Linux文件系統(tǒng)基本概念是什么”的內容了,經過本文的學習后,相信大家對Linux文件系統(tǒng)基本概念是什么這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關知識點的文章,歡迎關注!
本文標題:Linux文件系統(tǒng)基本概念是什么
本文路徑:http://weahome.cn/article/ghjgie.html