linux文件系統(tǒng)原理之一切皆文件

linux 文件系統(tǒng)工作原理簡介

文件系統(tǒng)時對存儲設(shè)備上的文件進(jìn)行組織管理的機制,組織方式不同就形成了不同的文件系統(tǒng)類型。

linux 中一切皆文件:不僅時普通的文件和目錄,連塊設(shè)備,套接字,管道等都是通過統(tǒng)一的文件系統(tǒng)來進(jìn)行管理的。

索引節(jié)點和目錄項

linux 系統(tǒng)為每個文件都分配了兩個數(shù)據(jù)結(jié)構(gòu)(索引節(jié)點 index node 和目錄項 dictionary entry),來對文件進(jìn)行管理。其中索引節(jié)點(index node)記錄文件的元數(shù)據(jù)信息,目錄項(dictionary entry)記錄文件的目錄結(jié)構(gòu)信息。

  • 索引節(jié)點:簡稱為inode,用來記錄文件的元數(shù)據(jù)信息:如inode編號,文件大小,訪問權(quán)限,修改日期,數(shù)據(jù)位置等。索引節(jié)點和文件一一對應(yīng),跟文件內(nèi)容一樣都會被持久化存儲到磁盤中,所以索引節(jié)點也同樣占據(jù)存儲空間。

  • 目錄項:簡稱為dentry,用來記錄文件的名字,索引節(jié)點指針以及其他目錄項的關(guān)聯(lián)關(guān)系。 多個關(guān)聯(lián)的目錄項構(gòu)成了文件系統(tǒng)的目錄結(jié)構(gòu),與索引節(jié)點不同,目錄項是內(nèi)核維護的一個內(nèi)存數(shù)據(jù)結(jié)構(gòu),所以也通常稱為目錄項緩存。

即索引節(jié)點是每個文件的唯一標(biāo)志,而目錄項維護的正是文件系統(tǒng)的樹狀結(jié)構(gòu)。目錄項和索引節(jié)點的關(guān)系是多對一,可以簡單理解為,一個文件可以有多個別名。如,通過硬鏈接為文件創(chuàng)建的別名,就會對應(yīng)不同的目錄項,不過這些目錄項本質(zhì)上還是鏈接同一個文件,所以,它們的索引節(jié)點是相同的。

文件數(shù)據(jù)的存儲方式

磁盤讀寫的最小單位是扇區(qū),然而扇區(qū)只有 512B 大小,如果每次都讀寫這么小的單位,效率一定很低。所以,文件系統(tǒng)又把連續(xù)的扇區(qū)組成了邏輯塊,然后每次都以邏輯塊為最小單元來管理數(shù)據(jù)。常見的邏輯塊大小為 4KB,由連續(xù)的 8 個扇區(qū)組成。

目錄項、索引節(jié)點以及文件數(shù)據(jù)的關(guān)系:

inode.png
  1. 目錄項本身就是一個內(nèi)存緩存,而索引節(jié)點則是存儲在磁盤中的數(shù)據(jù)。在前面的 Buffer 和 Cache 原理中,為了協(xié)調(diào)慢速磁盤與快速 CPU 的性能差異,文件內(nèi)容會緩存到頁緩存 Cache 中。所以這些索引節(jié)點自然也會緩存到內(nèi)存中,加速文件的訪問。

  2. 磁盤在執(zhí)行文件系統(tǒng)格式化時,會被分成三個存儲區(qū)域,超級塊、索引節(jié)點區(qū)和數(shù)據(jù)塊區(qū)。其中,

  • 超級塊,存儲整個文件系統(tǒng)的狀態(tài)。
  • 索引節(jié)點區(qū),用來存儲索引節(jié)點。
  • 數(shù)據(jù)塊區(qū),則用來存儲文件數(shù)據(jù)。

虛擬文件系統(tǒng)

目錄項,索引節(jié)點、邏輯塊以及超級塊構(gòu)成了linux文件系統(tǒng)的四大基本要素。另外,為了支持各種不同類型的文件系統(tǒng),linux內(nèi)核在用戶進(jìn)程和文件系統(tǒng)之間又加入了一個抽象層-- 虛擬文件系統(tǒng)VFS(virtual file system).

VFS 定義了一組所有文件系統(tǒng)都支持的數(shù)據(jù)結(jié)構(gòu)和標(biāo)準(zhǔn)接口。這樣,用戶進(jìn)程和內(nèi)核中的其他子系統(tǒng),只需要跟VFS提供的統(tǒng)一的接口進(jìn)行交互即可,而不需要關(guān)心各種底層的文件系統(tǒng)的實現(xiàn)細(xì)節(jié)。

系統(tǒng)調(diào)用,VFS,緩存,文件系統(tǒng)以及塊存儲之間的關(guān)系如下:

vfs.png

在VFS的下方,linux支持各種類型的文件系統(tǒng),如Ext4、XFS、NFS等。按照存儲位置的不同,文件系統(tǒng)可以分為三類:

  1. 基于磁盤的文件系統(tǒng),即把數(shù)據(jù)直接存儲在計算機本地掛載的磁盤中,常見又: Ext4, XFS, OverlayFS等類型的文件系統(tǒng)。

  2. 基于內(nèi)存的文件系統(tǒng),即常說的虛擬文件系統(tǒng)。此類文件系統(tǒng)不需要任何的磁盤分配空間,但會占用內(nèi)存,常見的有 /proc, /sys(主要向用戶空間導(dǎo)出層次化的內(nèi)核對象)等

  3. 網(wǎng)絡(luò)文件系統(tǒng),即用來訪問其他計算機數(shù)據(jù)的文件系統(tǒng),如NFS,SMB, iSCSI等。

以上提到的這些文件系統(tǒng),要先掛載到VFS目錄樹中的某個子目錄(掛載點),然后才能訪問其中的文件。如在安裝系統(tǒng)時,要先掛載一個根目錄(/),在根目錄下在把其他文件系統(tǒng)(其他磁盤分區(qū)、/proc文件系統(tǒng)、/sys文件系統(tǒng)、NFS等)掛載進(jìn)來。

文件系統(tǒng) IO

當(dāng)把文件系統(tǒng)掛載到掛載點之后,就能夠通過掛載點去訪問管理的文件了。 VFS提供了一組標(biāo)準(zhǔn)的文件訪問接口,以系統(tǒng)調(diào)用的方式提供給應(yīng)用程序使用。

如cat命令,首先調(diào)用open()打開一個文件;然后調(diào)用read(),讀取文件內(nèi)容;最后調(diào)用write()把文件內(nèi)容輸出到控制臺的標(biāo)準(zhǔn)輸出中。

strace cat vi
execve("/usr/bin/cat", ["cat", "vi"], 0x7ffcc99897c8 /* 48 vars */) = 0
brk(NULL)                               = 0x55b305e3d000
arch_prctl(0x3001 /* ARCH_??? */, 0x7ffdb0bafeb0) = -1 EINVAL (Invalid argument)
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=71314, ...}) = 0
mmap(NULL, 71314, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fa9e9c0b000
close(3)                                = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\360q\2\0\0\0\0\0"..., 832) = 832
pread64(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 784, 64) = 784
pread64(3, "\4\0\0\0\20\0\0\0\5\0\0\0GNU\0\2\0\0\300\4\0\0\0\3\0\0\0\0\0\0\0", 32, 848) = 32
pread64(3, "\4\0\0\0\24\0\0\0\3\0\0\0GNU\0\t\233\222%\274\260\320\31\331\326\10\204\276X>\263"..., 68, 880) = 68
fstat(3, {st_mode=S_IFREG|0755, st_size=2029224, ...}) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fa9e9c09000
pread64(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 784, 64) = 784
pread64(3, "\4\0\0\0\20\0\0\0\5\0\0\0GNU\0\2\0\0\300\4\0\0\0\3\0\0\0\0\0\0\0", 32, 848) = 32
pread64(3, "\4\0\0\0\24\0\0\0\3\0\0\0GNU\0\t\233\222%\274\260\320\31\331\326\10\204\276X>\263"..., 68, 880) = 68
mmap(NULL, 2036952, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fa9e9a17000
mprotect(0x7fa9e9a3c000, 1847296, PROT_NONE) = 0
mmap(0x7fa9e9a3c000, 1540096, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x25000) = 0x7fa9e9a3c000
mmap(0x7fa9e9bb4000, 303104, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x19d000) = 0x7fa9e9bb4000
mmap(0x7fa9e9bff000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1e7000) = 0x7fa9e9bff000
mmap(0x7fa9e9c05000, 13528, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fa9e9c05000
close(3)                                = 0
arch_prctl(ARCH_SET_FS, 0x7fa9e9c0a580) = 0
mprotect(0x7fa9e9bff000, 12288, PROT_READ) = 0
mprotect(0x55b3058ca000, 4096, PROT_READ) = 0
mprotect(0x7fa9e9c4a000, 4096, PROT_READ) = 0
munmap(0x7fa9e9c0b000, 71314)           = 0
brk(NULL)                               = 0x55b305e3d000
brk(0x55b305e5e000)                     = 0x55b305e5e000
openat(AT_FDCWD, "/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=5699248, ...}) = 0
mmap(NULL, 5699248, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fa9e94a7000
close(3)                                = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0), ...}) = 0
openat(AT_FDCWD, "vi", O_RDONLY)        = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=60, ...}) = 0
fadvise64(3, 0, 0, POSIX_FADV_SEQUENTIAL) = 0
mmap(NULL, 139264, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fa9e9485000
read(3, "{\n    \"registry-mirrors\": [\"http"..., 131072) = 60
write(1, "{\n    \"registry-mirrors\": [\"http"..., 60{
    "registry-mirrors": ["http://hub-mirror.c.163.com"]
}
) = 60
read(3, "", 131072)                     = 0
munmap(0x7fa9e9485000, 139264)          = 0
close(3)                                = 0
close(1)                                = 0
close(2)                                = 0
exit_group(0)                           = ?
+++ exited with 0 +++

文件讀寫方式的各種差異,導(dǎo)致了IO分類的多種多樣,最常見的有,緩沖于非緩沖IO,直接于非直接IO,阻塞于非阻塞IO,同步于異步IO。各種分類方式的依據(jù)如下:

  1. 根據(jù)是否利用了標(biāo)準(zhǔn)庫緩存,可以把文件IO分為緩沖于非緩沖IO。
  • 緩沖IO,利用標(biāo)準(zhǔn)庫緩存來加速文件的訪問,而標(biāo)準(zhǔn)庫內(nèi)部在通過系統(tǒng)調(diào)度訪問文件。

  • 非緩沖IO,直接通過系統(tǒng)調(diào)用來訪問文件,不經(jīng)過標(biāo)準(zhǔn)庫緩存。

所說的"緩沖",是指標(biāo)準(zhǔn)庫內(nèi)部實現(xiàn)的緩存,如很多程序遇到換行時才真正輸出,而換行前的內(nèi)容就是被標(biāo)準(zhǔn)庫暫時緩存了。

無論時緩沖IO還是非緩沖IO,它們最終都需要經(jīng)過系統(tǒng)調(diào)用來訪問文件,所以在系統(tǒng)調(diào)用后,還會通過頁緩存來減少磁盤的IO操作。

  1. 根據(jù)是否利用操作系統(tǒng)的頁緩存,可以把文件系統(tǒng)IO分為直接IO與非直接IO。
  • 直接IO,是指跳過操作系統(tǒng)的頁緩存,直接跟文件系統(tǒng)交互來訪問文件。

  • 非直接IO,是指進(jìn)行文件讀寫時要先經(jīng)過操作系統(tǒng)的頁緩存,然后再由內(nèi)核或額外的系統(tǒng)調(diào)用將文件內(nèi)容真正寫入磁盤。

如果要實現(xiàn)直接IO,需要再系統(tǒng)調(diào)用中指定O_DIRECT 標(biāo)志,如果沒有設(shè)置,默認(rèn)是非直接IO。另外不管是直接IO還是非直接IO,本質(zhì)上都是和文件系統(tǒng)的交互,在其他一些場景中(數(shù)據(jù)庫場景),還會有跳過文件系統(tǒng)直接讀寫磁盤的場景(裸IO)。

  1. 根據(jù)應(yīng)用程序是否阻塞自身運行,可以把文件IO分為阻塞IO和非阻塞IO。
  • 所謂阻塞IO,是指應(yīng)用程序執(zhí)行IO操作后,如果沒有獲得響應(yīng),就會阻塞當(dāng)前線程,不能執(zhí)行其他任務(wù)。

  • 非阻塞IO與之相反,是指應(yīng)用程序進(jìn)行IO操作后,不會阻塞當(dāng)前的線程,可以執(zhí)行其他任務(wù),隨后再通過輪詢或者事件通知的形式,獲取調(diào)用結(jié)果。

如訪問管道或者網(wǎng)絡(luò)套接字時,設(shè)置了O_NONBLOCK標(biāo)志,就表示用非阻塞的方式進(jìn)行訪問,如果不做任何設(shè)置,默認(rèn)是阻塞的方式進(jìn)行訪問。

  1. 根據(jù)是否等待響應(yīng)結(jié)果,可以把文件IO分為同步和異步IO。
  • 同步IO,應(yīng)用程序執(zhí)行IO操作后,要一直等到整個IO完成后才能獲得IO的響應(yīng)。

  • 異步IO,應(yīng)用程序執(zhí)行IO操作后,不用等到完成和完成后的響應(yīng),而是繼續(xù)執(zhí)行即可,等到此次IO完成后,響應(yīng)會通過事件通知的方式告知應(yīng)用程序。

如,再操作文件時如果設(shè)置了O_SYNC或者O_DSYNC標(biāo)志,就代表同步IO, 如果設(shè)置了O_DSYNC,需要等文件數(shù)據(jù)寫入磁盤后才能返回,而O_SYNCO_DSYNC的基礎(chǔ)上,要求文件元數(shù)據(jù)也要寫入磁盤后才能返回。

在訪問管道或者套接字時, O_ASYNC設(shè)置選項標(biāo)志IO是異步操作,此時內(nèi)核會通過SIGIO 或者SIGPOLL信號來通知進(jìn)程文件是否可寫。

很多IO的概念頁出現(xiàn)在網(wǎng)絡(luò)編程中,如非阻塞IO通常會跟select/poll配合使用。

至此,可以理解“l(fā)inux 一切皆文件” 的含義:無論是普通文件和塊設(shè)備、還是網(wǎng)絡(luò)套接字和管道等,它們都是通過統(tǒng)一的VFS接口來訪問的。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 同 CPU、內(nèi)存一樣,磁盤和文件系統(tǒng)的管理,也是操作系統(tǒng)最核心的功能。 磁盤為系統(tǒng)提供了最基本的持久化存儲。 文件...
    tracy_668閱讀 499評論 0 5
  • 今天感恩節(jié)哎,感謝一直在我身邊的親朋好友。感恩相遇!感恩不離不棄。 中午開了第一次的黨會,身份的轉(zhuǎn)變要...
    余生動聽閱讀 10,916評論 0 11
  • 彩排完,天已黑
    劉凱書法閱讀 4,499評論 1 3
  • 沒事就多看看書,因為腹有詩書氣自華,讀書萬卷始通神。沒事就多出去旅游,別因為沒錢而找借口,因為只要你省吃儉用,來...
    向陽之心閱讀 4,987評論 3 11
  • 表情是什么,我認(rèn)為表情就是表現(xiàn)出來的情緒。表情可以傳達(dá)很多信息。高興了當(dāng)然就笑了,難過就哭了。兩者是相互影響密不可...
    Persistenc_6aea閱讀 129,919評論 2 7

友情鏈接更多精彩內(nèi)容