前言
在上篇博客當中,我們對 文件系統 和 inode 做了初步了解,本博客將在上篇博客的基礎之上,對于 文件系統當中的目錄進行進步一闡述。
Linux - 進一步理解 文件系統 - inode - 機械硬盤-CSDN博客
目錄
?一個文件有一個 inode,每一個 inode 都是有自己的 inode 編號(這個inode 編號只在自己當前所在分區當中有效)。
inode 的劃分是以 分區為單位的,也就是說,各個分區當中的 inode 是獨立的,inode 編號也是獨立的。
雖然,inode 當中存儲了 這個文件的所有屬性,但是在這個inode 當中不會存儲 文件名的。也就是說,文件名壓根就不屬于 文件屬性。
換言之,如果我們想要訪問一個文件,如果這個文件名是在 inode 當中存儲的,那么對于操作系統來說,用戶就要告訴這個 操作系統 ,inode 是多少,才能拿到 文件名。
但是,如果是 小白用戶,壓根就不知道 inode 的存在,他知道 文件名,而且,我們日常在訪問文件,修改文件,查找文件 的基本都是通過 文件名來操作的。?
使用者從來沒有關心過 inode 這個是什么,使用者對于操作文件都是通過 文件名來操作的。
而,文件名肯定是有重復的,操作系統如何識別這些重復的文件,其實靠的就是 目錄,我們知道,有絕對路徑 和 相對路徑來找到某一個文件。
所以,其實我們是通過 目錄來找到各個文件的。
那么目錄是什么呢?
其實目錄本質上也是一個文件:
目錄也是文件,目錄也有自己獨立的 inode。也就是說,目錄也有自己的 屬性。
那么,在目錄當中有內容嗎?
答案是有的。那么目錄這個文件當中存儲的是什么呢?
?目錄的數據塊當中,存儲的是 目錄當中的文件的 文件名 和 各個文件對應的 inode 的映射關系。
?所以,一個文件的文件名不是存儲在 這個文件的屬性(inode)當中的,文件的文件名不是這個文件的屬性,這個文件的文件名 和 這個文件對應的 inode 的映射關系 是存儲在當前文件所以在目錄的內容當中的。
?所以,比如 ls 這個命令,在查找當前目錄下的 文件和 目錄的話,其實就是在當前目錄的內容當中找到 本目錄下的 文件名 和 各個文件映射的 inode 關系,根據命令行參數選項,打印出這個文件對應的信息即可。
所以,如果我們想要進入某一個目錄當中,那么這個目錄就要有 x 權限;當我們在某一個目錄當中 創建一個新的文件,或者是 要刪除某一個文件,需要這個目錄有 w 權限。
?因為,就算我們在一個沒有 w 權限的目錄 執行路徑 當中創建一個了一個新的文件,但是,這個文件的 文件名 和 這個文件對應的 inode 映射關系是不能再 保存在 這個 目錄文件對應的數據塊當中。
?同樣,如果是這個目錄是沒有 r (讀)權限的,那么這個目錄當中的文件是不能訪問的,因為 要像訪問一個文件,或者是修改一個文件,那么就要拿到這個文件的 inode 。但是,因為目錄文件是不給讀的,所以拿不到想訪問文件的inode,那拿不到 inode 怎么訪問文件呢?
而像是 絕對路徑和相對路徑,也是同一個 根目錄 或者是 當前目錄 文件的數據塊當中,一層一層遞歸的方式來尋找的。
所以,如果我們要想訪問 當前目錄當中的某一個目錄的話,就需要找到這個目錄的 inode,才能訪問到這個目錄文件。
但是,要先找到這個 目錄的 inode ,就要在這個目錄的上一層的 目錄文件的數據塊當中找到這個目錄文件的 inode映射關系。
到這你可能就會想,那么這不就遞歸了嗎?我們要想找到這個目錄的 inode 就要一直往上去遞歸式的去尋找。
是的,是遞歸式的尋找,但是,不是無窮無盡的遞歸,因為 我們從任何路徑當中 往上來遞歸式尋找的話,一定可以找到一個目錄 --- 根目錄。
所以的目錄都是從 根目錄 衍生出來的。
所以,絕對路徑就是 先遞歸式的返回遍歷到 根目錄,再根據 給出的絕對路徑 來找到 對應文件。
?相對路徑就更簡單了,只要是當前目錄路徑已經被找到了,只需要按照相對路徑當中給出的路徑來進行查找即可。
得出結論:
- 在Linux 當中,訪問任何一個 路徑都需要帶上路徑,可能你在使用 ls 等等這些命令的時候,沒有帶上路徑,但是同樣也訪問到了 目錄文件,或者是文件當中的內容。其實這些命令訪問文件也是要 路徑的,只不過,我們可以通過設置一些環境變量來 提前保存一些路徑,這些我們稱之為 -- 默認路徑,系統就會默認從 這個路徑當中來訪問文件,但是其實本質上也是 通過路徑來訪問到內容的。
?
?而,像上述要像遞歸到根目錄的方式,來查找 文件,這種方式太慢了,所以,在 Linux 當中,會把我們曾經訪問過的,或者是經常訪問的?若干目錄, 已經這些目錄當中的若干信息(比如 文件映射的 inode,文件名等等信息),緩存一份。---- dentry緩存。
當我們需要訪問 緩存當中存在的文件之時,就可以直接從 緩存當中讀取到 這個文件的 inode 等等信息,直接訪問到這個文件了,不需要再去遞歸式的尋找 文件位置。
軟鏈接? 和 硬鏈接
?我們先來看是如何創建一個 文件的 軟鏈接 和 硬鏈接的:
軟鏈接;
上述就是創建一個 軟鏈接,此時就有一個 text_link 指向 text.c 文件了。
如果你查看這個 text_link 的屬性,你會查看到 這個 text_link 有 inode ,說明這個 text_link是一個文件,而且,在這個文件的后面還有一個? 數字,你可以看到是1:
?
你可以發現這個 text_link是有 inode的 ,說明這個 text_link是一個 文件,而且,在 這個 text_link 和 text.c 兩個文件的訪問權限 后面 還有一個 數字1 ,這個數字1 我們在后面 說到 硬鏈接的時候再敘述。
所以,此時也就是相當于是 有一個 text_link 軟鏈接文件指向了 text.c 這個文件。
硬鏈接:
像上述就生成一個 硬鏈接文件。
?同時,這個 新生成的 text_link 硬鏈接文件,也是有 inode 的,但是這個 inode 是和 text.c 文件是一樣的,說明這個 硬鏈接不是一個獨立的文件,而且,此時,在 text_link 這個文件的 訪問權限符 后面的 數字,變成了2。
而且,相信你還注意到 ,我們對應生成目標文件的 硬鏈接文件的目標文件,也就是 text.c 這個文件。在文件訪問權限符之后的數字,在生成 硬鏈接文件之前,本來是 1 的,但是在生成 硬鏈接文件之后就變成了2。
對于上述的結果,我們先不做闡述,我們下來看看 生成 軟鏈接文件 和 硬鏈接文件之間的語法是什么:
其實,都是使用 ln 這個語法,但是嗎,如果是軟鏈接文件,需要帶上 -s 這個選項參數,其實這里的 -s 就是?Soft 軟的這個單詞的縮寫。
如不帶上 -s 這個選項那么,默認就是 硬鏈接文件的生成方式。、
創建軟鏈接和硬鏈接的語法:
?
ln -s 被指向文件名 生成的目標指向文件的軟鏈接文件名
ln 被指向文件名 生成的目標指向文件的軟鏈接文件名ln -s text.c text_link #生成一個text_link 軟鏈接文件指向 text.c 文件
ln text.c textlink #生成一個text-link 硬鏈接文件指向 text.c 文件
我們先來說說,上述所說的 在文件訪問權限符之后的 數字代表的是什么意義?
其實這個數代表的意義是代表這個文件當前的硬鏈接個數。(其實就是 當前 inode 的使用文件的引用計數)
?其實,此時,如果我們把同一個文件的 軟鏈接文件 和硬鏈接文件 ,都創建出來:
?
你會發現,硬鏈接文件(上圖的text-link文件) 和 鏈接的文件(上圖的text.c文件)?的 inode 是一樣的。
但是,軟連接文件的 inode 和 其他兩個文件的 inode 是不一樣的。?
硬鏈接不是一樣的獨立的文件,因為硬鏈接文件 沒有獨立的 inode。
理解硬鏈接
?因為硬鏈接文件的 inode 和 鏈接的文件的inode是一樣的。所以,這兩個文件的屬性應該是一樣的。
這里也側面的證明了 ,文件的文件名是不在 文件的inode 當中存儲的,而是在目錄當中存儲的。
而,所謂的建立硬鏈接,本質上其實就是在特定的 目錄的數據塊當中,新增 文件名 和 文件和inode 的映射關系。
簡答來說,就是在 目標文件所在目錄的內容當中(也就是在目錄的數據塊當中),把 新按照目標文件生成的 硬鏈接文件的 文件名和 對應的 inode 映射關系,保存到?目標文件所在目錄的內容當中。
?如果此時,我們在 硬鏈接文件存在的情況下,刪除 這個硬鏈接文件鏈接的 目標文件的話,會出現什么結果呢?
?上述是結果,下述是刪除 text.c 文件之前的結果:
發現,就算我們刪除了text.c 這個文件,但是,硬鏈接文件并沒有失效,inode 還是和之前一樣,跟刪除的 目標文件的 inode 保持一致,并沒有發生改變。
?但是,引用計數 變成了1,因為此時 硬鏈接個數又變成了1個。
?像上述這種,先創建一個文件的 硬鏈接文件,然后刪除掉這個文件,保存這個文件的硬鏈接文件,這個操作被稱之為 -- 取別名。
所以,在每一個 inode 內部,都有一個 作用于 當前 inode 硬鏈接個數的 引用計數。
?而,在目錄當中,保存了 每一個 文件名 對應 映射的 inode 的信息。
?可以存在 不同的文件名,映射到同一個 inode 當中。
所以,現在我們可以有一個更好的對于 引用計數的概念,不在是 硬鏈接文件個數了,而是 有多少個 文件名 映射該 inode。
理解軟鏈接
軟鏈接文件當中,你可以發現,其實軟鏈接文件是一個新創建的獨立的文件。因為在創建之后,有獨立的 inode。所以,軟鏈接不會影響 鏈接的目標文件的 引用計數。
那么既然有 獨立的 inode ,也就意味著有獨立的 數據塊,也就是說有獨立的存儲空間,在這個文件當中要存儲什么呢?
存儲的是 指向的目標文件的路徑。
其實你可以理解為 在軟鏈接當中存儲的是 ,指向目標文件的 指針,通過這個指針可以訪問到 這個軟鏈接指向的目標文件。
發現,在text.c 當中重定向的字符串 aaaaa ,通過 text-link 這個軟鏈接文件也可以訪問到。
?所以,既然存儲的是指針,那么就會有 野指針的情況,當我們把軟鏈接指向的?目標文件 刪除之時,那么這個 軟鏈接文件當中存儲的指針就會失效、
如上所說,從藍色的軟連接文件名,變成了紅色的,此時代表的意思就是已經出現連接錯誤了。
?其實,這個軟鏈接特別像 windows 當中 程序的快捷方式。
?當我們查看這些 程序的快捷方式的屬性的話,可以看見一個 目標的屬性:
其實這個就是我們上述所說的,軟鏈接當中存儲的是 指向目標文件的 路徑。?其實是一樣的 。
為什么要有 軟硬鏈接
?我們日常使用的程序軟件,其實都不簡簡單單是一個 xx.exe 這種直接點擊就能運行的程序。整個程序一般還是有自己的 配置文件,或者是程序運行所需要的文件數據信息,所以,往往一個程序的 xx.exe 是藏在一個較深的路徑當中:
?
而,如果我們想在當前目錄下(不在這個程序的 xx.exe 文件下)直接運行這個程序的話,就必須要帶上絕對路徑或者是 相對路徑。
但是類似 D:/ProgramFiles(x86)/Huorong/Sysdiag/bin/xxx.exe 這行來運行這個可執行程序就太麻煩了,所以,可以使用 軟連接的方式 創建快捷方式,來調用這個可執行程序:
?
?同樣,按照上述的方式,我們可以在Linux PATH 環境變量當中創建出我們自己,或者是第三方的 程序的 軟鏈接文件,這樣就可以直接 輸入 文件名,不用的現去在當前目錄下創建 對應的 軟鏈接文件,都可以直接 調用 我們想安裝的 程序的可執行文件了:
?
此時我們就可以直接,在任何路徑下調用這個 我們剛剛安裝的 程序了:
?
?所以,這個軟件安裝到哪里都可以,只要在 PATH 環境變量 指向的 系統默認的 路徑目錄當中創建了 這個程序的 軟鏈接程序,就可以隨時隨地調用這個程序了。
其次,當你創建了一個新的目錄,那么你會發現,這個目錄的 硬鏈接個數是 2 個。其實你應該已經猜到為什么了:
?
?在上圖當中,就有一組 inode 和 文件名 的關系,就是當前的dir 這個目錄文件名 和 這個文件的inode 的映射關系。現在我們進入到這個目錄當中,查看這個目錄當中所有文件,包括隱藏文件:
?
有一個 "." 作為文件名的 文件,很多讀者應該知道,這個 "." 文件,代表的是當前所在 所在目錄的這個目錄的文件。
也就是說,在 dir 這個目錄當中的 "." 這個文件,和 dir 這個目錄文件 所映射的 inode 是一樣的,兩個映射的是同一個 inode,代表的是同一個文件。
而且如上圖所示,你可以發現 dir 和 dir當中的 "." 這兩個文件名的 inode 是相同的。
所以,這里你就可以理解了 ,為了 每一個目錄當中的 "." 文件,都可以代表的是 當前目錄文件。
上述也提到了 ".." 兩個點的文件名,這個文件代表的是當前目錄的 上級目錄文件,和上級目錄文件共用的是同一個inode。
所以,按照這個推理的話,在上述創建 dir 這個目錄 所在目錄,應該就有 3 個 硬鏈接個數了,因為在 dir 當中還有一個 ".." 文件是映射的 這個目錄:
?
如上圖所示,ln_text 目錄就是 dir 所在的目錄,是三個 硬鏈接個數。
?同樣你可以查看 "/" 根目錄 的 硬鏈接個數:
?
?發現是一個很大數字,因為在 "/" 目錄當中已經創建了很多個 目錄了,每一個目錄當中都有一個 ".." 硬鏈接上 "/" ,所以才會有這么多。
所以,往后,如果想知道某一個 目錄當中有多少個 有效目錄(也就是想上述 dir 這樣目錄)其實可以像上訴一樣查看這個目錄的 硬鏈接個數,該目錄的 硬鏈接個數 - 2 就是這個 目錄當中的有效目錄。
?Linux 當中的目錄結構是一個多叉樹,Linux 當中的文件系統是利用 上述的 硬鏈接方式,來 維持多叉樹當中每一個 結點當中的有 parent指針 和 指向當中結點的指針,這樣一個關系。我們稱之為 -- 路徑定位。實現目錄間的切換。
然后使用在目錄當中存儲的 文件名 和 文件名所映射的 inode 來位置當前結點的孩子結點直接的關系。
在上述你可能有疑問,為什么可以直接用一個目錄的 硬鏈接個數來確定其中的 有效目錄個數?
就不怕 用戶自己使用 ln 這個指令來對 這個目錄文件進行 硬鏈接嗎?
其實不怕。因為 目錄是不能使用 ln 創建硬鏈接的。
如上所示,我們發現報錯了,報錯信息是? 鏈接不允許是 目錄。?
但是,目錄是可以建立 軟鏈接的。
?這里需要注意是:如果你想刪除某一個 軟鏈接的話,可以使用 rm 命令,但是有的時候是刪不掉的,這個時候更多的使用的事 unlink 軟鏈接文件名 這樣的方式來進行刪除軟鏈接文件的。
為什么Linux 不給 目錄建立硬鏈接?
?我們可以假設一下,然后來考慮為什么。其實很簡單,就是一個循環引用計數的問題。
如上圖所示,dir 目錄文件是 ln_text 目錄文件的硬鏈接,dir 就相當于是我們在某一個目錄下 創建的一個ln_text 目錄文件的 硬鏈接文件。
那么在上述 多叉樹文件系統當中,如果我想從 ln_text 找到 dir 這個目錄的話,從上圖當中的紅色路徑就可以找到,但是,當我們找到 dir 之后,它的 inode 又是 2(和 ln_text 是一樣的),所以此時就會回去找到 ln_text 目錄,?又回到 最初的起點,但是還是沒有找到,所以又會像之前一樣往下遞歸找到 dir 目錄·······
這不就是一個循環了嗎?
而,上述說過 "." 和 ".." 兩個文件不也是建立了 硬鏈接嗎?
這兩個文件不是用戶建立的,是操作系統自己建立的,它在建立之初,因為 這兩個文件和自己硬鏈接的文件 不會像上個例子一樣 隔個十萬八千里,人家就是表示的當前目錄和 上級目錄,所以這個在操作系統內部可以實現。但是如果放在很長的 文件系統多叉樹結構當中就不好實現了。
換句話說,操作系統自己能夠 實現目錄的硬鏈接,本質上其實是 操作系統只相信自己,不相信任何人,包括 root 。
?同樣你也可以發現,關于 "." 和 ".." 這兩個文件是不能做 搜索操作的嗎,只能在 路徑定位當中使用。
換言之,操作系統之所以要 才上 循環引用的坑也要 弄出?"." 和 ".." 這兩個文件,就是為了引出 相對路徑這個概念,讓我們更好的使用 文件系統。