1. Linux文件系統(tǒng)
在
Windows操作系統(tǒng)中,使用驅(qū)動(dòng)器盤(pán)符(比如C盤(pán)),決定文件的路徑名。每個(gè)驅(qū)動(dòng)器都會(huì)有自己的目錄結(jié)構(gòu),以便訪(fǎng)問(wèn)文件。
Linux的路徑名不使用驅(qū)動(dòng)器盤(pán)符。而是將文件存儲(chǔ)在單個(gè)目錄結(jié)構(gòu)中,這個(gè)目錄結(jié)構(gòu)稱(chēng)為虛擬目錄 virtual directory。虛擬目錄將所有的文件目錄納入到單個(gè)目錄結(jié)構(gòu)中。
Linux虛擬目錄結(jié)構(gòu)只包含了一個(gè)稱(chēng)為根(Root)目錄的基礎(chǔ)目錄。
注意:
Linux使用正斜線(xiàn)
(/)而不是反斜線(xiàn)(\)在文件路徑中劃分目錄。在Linux中,反斜線(xiàn)用來(lái)標(biāo)識(shí)轉(zhuǎn)義字符。無(wú)論是window環(huán)境還是Linux環(huán)境,我們關(guān)注的還是java獲取路徑的方法。
Java獲取文件路徑:
public static void main(String[] args) throws IOException {
System.out.println("系統(tǒng)默認(rèn)的分隔符:"+File.separator);
File emptyFile = new File("");
System.out.println("項(xiàng)目路徑:"+System.getProperty("user.dir"));
//返回抽象路徑名的絕對(duì)路徑字符串
System.out.println("absolutePath路徑為空時(shí):"+emptyFile.getAbsolutePath());
System.out.println("canonicalPath路徑為空時(shí):"+emptyFile.getCanonicalPath());
System.out.println();
//路徑是.或者..時(shí)候
File file1 = new File(".");
System.out.println("absolutePath路徑為.時(shí):"+file1.getAbsolutePath());
System.out.println("canonicalPath路徑為.時(shí):"+file1.getCanonicalPath());
System.out.println();
File file2 = new File("..");
System.out.println("absolutePath路徑為..時(shí):"+file2.getAbsolutePath());
System.out.println("canonicalPath路徑為..時(shí):"+file2.getCanonicalPath());
//路徑是絕對(duì)路徑時(shí)
System.out.println();
File absoluteFile = new File("/");
System.out.println("absolutePath路徑為絕對(duì)路徑時(shí):"+absoluteFile.getAbsolutePath());
System.out.println("canonicalPath路徑為絕對(duì)路徑時(shí):"+absoluteFile.getCanonicalPath());
}
執(zhí)行結(jié)果:

在Linux中,會(huì)看到下面這種路徑:
/home/Rich/Document/test.doc
這表明文件test.doc位于Document目錄, Document又位于Rich目錄之下,Rich則是在home路徑之下的。路勁本身并沒(méi)有提供任何有關(guān)文件究竟存放在哪個(gè)物理磁盤(pán)的信息。
1.2 Linux如何協(xié)調(diào)管理各個(gè)存儲(chǔ)設(shè)備
Linux不使用驅(qū)動(dòng)盤(pán)符,而是將文件存儲(chǔ)在單個(gè)目錄結(jié)構(gòu)中,這個(gè)目錄結(jié)構(gòu)稱(chēng)為
虛擬目錄。而Linux第一塊硬盤(pán)。即根驅(qū)動(dòng)器,包含了虛擬目錄的核心,其他目錄都是在這構(gòu)建的,可以?huà)燧d一些存儲(chǔ)設(shè)備,我們稱(chēng)之為掛載點(diǎn)。
Linux會(huì)在根驅(qū)動(dòng)器上創(chuàng)建一些特別的目錄,我們稱(chēng)之為掛載點(diǎn)(mount point),掛載點(diǎn)是虛擬目錄中用于分配額外存儲(chǔ)設(shè)備的目錄。虛擬目錄會(huì)讓文件和目錄出現(xiàn)在這些掛載點(diǎn)目錄中,然而實(shí)際上他們卻存儲(chǔ)在另外一個(gè)存儲(chǔ)器中。
虛擬目錄和掛載:
- 虛擬目錄結(jié)構(gòu)只包含一個(gè)稱(chēng)為
根目錄的基礎(chǔ)目錄。 - Linux第一塊硬盤(pán)是
根驅(qū)動(dòng)器,是虛擬目錄的核心,其他目錄都是在這掛載的。 - Linux在根驅(qū)動(dòng)器上創(chuàng)建一些特別的目錄,就是掛載點(diǎn)。掛載點(diǎn)是
虛擬目錄下分配額外存儲(chǔ)設(shè)備的目錄。文件和目錄存儲(chǔ)在掛載點(diǎn)目錄上,實(shí)際上卻存儲(chǔ)在另外的驅(qū)動(dòng)器上。 - 訪(fǎng)問(wèn)一個(gè)路徑時(shí),會(huì)選擇一個(gè)能
最大匹配當(dāng)前路徑前綴的掛載點(diǎn)。比如說(shuō),有/var的掛載點(diǎn),也有/var/run的掛載點(diǎn)的情況下,訪(fǎng)問(wèn)/var/run/test.pid,就會(huì)匹配到/var/run掛載點(diǎn)設(shè)備下面的/test.pid; - 訪(fǎng)問(wèn)非掛載路徑的時(shí)候,按照4中所說(shuō),其實(shí)就是訪(fǎng)問(wèn)最接近的一個(gè)掛載點(diǎn),如果沒(méi)有其他掛載點(diǎn)就訪(fǎng)問(wèn)根驅(qū)動(dòng)器上的目錄或者文件。
- 通過(guò)掛載點(diǎn),程序不需要嚴(yán)格區(qū)分子目錄和獨(dú)立設(shè)備了。這樣比如/usr可以是/的子目錄,也可以是一個(gè)獨(dú)立的分區(qū),管理起來(lái)很靈活。
所謂掛載,就是可以通過(guò)文件系統(tǒng)來(lái)訪(fǎng)問(wèn)到你存儲(chǔ)設(shè)備里面的東西。
通常系統(tǒng)文件會(huì)存儲(chǔ)在根驅(qū)動(dòng)器上,而用戶(hù)文件則存儲(chǔ)在另一驅(qū)動(dòng)器中。

需要和windows的文件系統(tǒng)區(qū)分開(kāi)來(lái)
不是所有邏輯上等的父子關(guān)系都必須是同一設(shè)備。。。
決定不同路徑對(duì)應(yīng)到不同設(shè)備的機(jī)制就叫做掛載mount。。
通過(guò)mouth,可以設(shè)置當(dāng)前路徑與設(shè)備的對(duì)應(yīng)關(guān)系。
你把linux目錄結(jié)構(gòu)想象成一棵樹(shù),目錄就是樹(shù)枝,分區(qū)就是籃子,掛載就是把籃子掛在樹(shù)枝上,這樣你沿著樹(shù)枝就能進(jìn)到籃子里獲取籃子里的內(nèi)容。如果籃子沒(méi)有被掛載到樹(shù)枝上,你順著樹(shù)枝只能拿到一個(gè)“空”,也就是null。
1.3 常見(jiàn)的Linux目錄結(jié)構(gòu)
Linux文件系統(tǒng)是從Unix文件系統(tǒng)結(jié)構(gòu)演進(jìn)過(guò)來(lái)的。在Linux文件系統(tǒng)中,通用的目錄名用于表示一些常見(jiàn)的功能。

常見(jiàn)的目錄名均基于文件系統(tǒng)層級(jí)標(biāo)準(zhǔn)(FHS)。很多Linux發(fā)行版都遵循了FHS。深入理解linux系統(tǒng)的目錄結(jié)構(gòu)(總結(jié)的非常詳細(xì))
常見(jiàn)的Linux頂級(jí)虛擬目錄名及內(nèi)容:
| 目錄 | 用途 |
|---|---|
| / | 虛擬目錄的根節(jié)點(diǎn),通常不會(huì)存儲(chǔ)文件 |
| /bin | 用于存放普通用戶(hù)可執(zhí)行的命令 |
| /boot | 用于存放Linux啟動(dòng)必需的文件 |
| /dev | 用于存放系統(tǒng)的設(shè)備信息 |
| /etc | 用于存放系統(tǒng)的各種配置 |
| /home | 用于存放系統(tǒng)各個(gè)普通用戶(hù)的主目錄 |
| /lib | 用于存放系統(tǒng)和應(yīng)用程序共享庫(kù)文件 |
| /lost+found | 文件損壞后找回的文件存放目錄 |
| /media | 媒體目錄,掛載光盤(pán),U盤(pán)等文件系統(tǒng)目錄 |
| /misc | 用來(lái)掛載NFS共享目錄 |
| /mnt | 掛載目錄,掛載其他硬盤(pán)分區(qū)系統(tǒng)的目錄 |
| /opt | 可選目錄,第三方軟件包和數(shù)據(jù)文件 |
| /root | root用戶(hù)主目錄,普通用戶(hù)無(wú)法訪(fǎng)問(wèn) |
| /sbin | 存放只有root用戶(hù)才能運(yùn)行的系統(tǒng)管理工具 |
| /tmp | 臨時(shí)目錄 |
| /usr | 用來(lái)存放供所有用戶(hù)使用的各種應(yīng)用程序和數(shù)據(jù)文件目錄,該目錄中也有對(duì)應(yīng)的/bin、/lib、/etc等目錄 |
| /var | 可變目錄,用于存放系統(tǒng)中經(jīng)常變化的文件 |
需要注意的是:我們?cè)谝话闳粘D芙?jīng)常訪(fǎng)問(wèn)的目錄:/home、/mnt、/media、/usr
2. Linux文件系統(tǒng)層次

-
Application Process:用戶(hù)層就是我們?nèi)粘J褂玫母鞣N程序,需要的接口主要是文件的創(chuàng)建,刪除,打開(kāi),關(guān)閉,寫(xiě),讀。 -
Virtual File System VFS層: Linux系統(tǒng)分為用戶(hù)態(tài)和內(nèi)核態(tài),用戶(hù)態(tài)請(qǐng)求硬件需要調(diào)用system call通過(guò)內(nèi)核態(tài)去實(shí)現(xiàn)。用戶(hù)這些文件操作都有著對(duì)應(yīng)的system call函數(shù)接口,接口調(diào)用VFS對(duì)應(yīng)的函數(shù)。 -
文件系統(tǒng)層:不同的文件系統(tǒng)實(shí)現(xiàn)了VFS的這些函數(shù),通過(guò)指針注冊(cè)到VFS里面,所以,用戶(hù)的操作通過(guò)VFS轉(zhuǎn)到各種文件系統(tǒng)。文件系統(tǒng)把文件讀寫(xiě)命令轉(zhuǎn)化成對(duì)磁盤(pán)的LBA(尋址)操作,起到一個(gè)翻譯和磁盤(pán)管理的作用。 -
Page Cache:文件系統(tǒng)底下有緩存,Page Cache,加速性能。對(duì)磁盤(pán)LBA的讀寫(xiě)數(shù)據(jù)緩存到這里。 -
Block Device Driver 塊設(shè)備區(qū):用來(lái)訪(fǎng)問(wèn)磁盤(pán)LBA的層級(jí),讀寫(xiě)命令組合之后插入到命令列,磁盤(pán)驅(qū)動(dòng)從隊(duì)列讀命令執(zhí)行。 -
磁盤(pán)驅(qū)動(dòng)層:磁盤(pán)的驅(qū)動(dòng)程序把対LBA的讀寫(xiě)命令轉(zhuǎn)化為各自的協(xié)議,比如ATA命令,SCSI命令,或者是自己硬件可以識(shí)別的自定義命令,發(fā)送給磁盤(pán)控制器。 -
磁盤(pán)物理層:讀寫(xiě)物理數(shù)據(jù)到磁盤(pán)介質(zhì)。
2.1 Virtual File System(虛擬目錄系統(tǒng)):
VFS并不是實(shí)際的文件系統(tǒng)。它只存在于內(nèi)存中,不存在于任何外存空間,VFS在系統(tǒng)啟動(dòng)時(shí)創(chuàng)建,在系統(tǒng)關(guān)閉時(shí)被銷(xiāo)毀。
VFS的作用:就是屏蔽各類(lèi)文件系統(tǒng)的差異,給用戶(hù)、應(yīng)用程序、甚至Linux其他管理模塊提供統(tǒng)一的接口集合。
VFS擁有關(guān)于各種特殊文件系統(tǒng)的公共界面,當(dāng)某個(gè)進(jìn)程發(fā)布一個(gè)面向文件系統(tǒng)調(diào)用時(shí),內(nèi)核將調(diào)用VFS中對(duì)應(yīng)的函數(shù),這個(gè)函數(shù)處理一些與物理結(jié)構(gòu)無(wú)關(guān)的操作,并把它重定向?yàn)檎鎸?shí)文件系統(tǒng)中相應(yīng)的函數(shù)調(diào)用。后者處理那些與物理結(jié)構(gòu)相關(guān)的操作。

用戶(hù)層只能于VFS打交道,而不能直接訪(fǎng)問(wèn)實(shí)際文件系統(tǒng),比如EXT2、EXT3、PROC。
換句話(huà)說(shuō),用戶(hù)層不用也不能區(qū)別對(duì)待真正的文件系統(tǒng)。
不過(guò)SOCKET雖然也屬于VFS的管籌范圍,但是有其特殊性,就是不能像打開(kāi)打開(kāi)大部分系統(tǒng)的“文件”一樣打開(kāi)socket,它只能被創(chuàng)建,而且在內(nèi)核中對(duì)其特殊性處理。
2.2 文件系統(tǒng)與工作原理
在Linux中文件系統(tǒng)的種類(lèi)很多,主要是VFS做了一個(gè)軟件抽象層,向上提供文件操作接口,向下提供標(biāo)準(zhǔn)接口供不同文件系統(tǒng)對(duì)接。下面以EXT4文件系統(tǒng)為例,講解下文件系統(tǒng)結(jié)構(gòu)與工作原理。
存儲(chǔ)系統(tǒng)和inode
數(shù)據(jù)是保存在磁盤(pán)上的,磁盤(pán)上最小的存儲(chǔ)數(shù)據(jù)是扇區(qū),每個(gè)扇區(qū)一般都是可以存放512字節(jié)的數(shù)據(jù)。
那么如果數(shù)據(jù)大于521字節(jié)的時(shí)候,磁盤(pán)需要不停地移動(dòng)磁頭來(lái)尋找數(shù)據(jù),一個(gè)文件很容易超過(guò)512字節(jié),那么如果將多個(gè)扇區(qū)合并成一個(gè)塊,那么磁盤(pán)就可以提高效率。磁頭一次讀取多個(gè)扇區(qū)就為一個(gè)塊"block"(Linux上稱(chēng)為塊,windows上稱(chēng)為簇)。
一個(gè)塊多為4KB,因?yàn)閴K是文件系統(tǒng)上的概念,所以塊也可以在格式化時(shí)候自行定義。
文件數(shù)據(jù)都存儲(chǔ)在“塊”中我們還必須找到一個(gè)地方存儲(chǔ)文件的元信息,比如文件的創(chuàng)建者、文件的創(chuàng)建日期、文件的大小等等,這種存儲(chǔ)文件元信息的區(qū)域就叫做inode(索引節(jié)點(diǎn))。
每個(gè)分區(qū)都有自己的文件系統(tǒng),也有一套屬于自己的inode。
分析EXT4文件系統(tǒng)

- 引導(dǎo)塊:磁盤(pán)分區(qū)的第一個(gè)塊,記錄文件系統(tǒng)分區(qū)的一些信息,引導(dǎo)加載當(dāng)前分區(qū)的程序和數(shù)據(jù)被保存在這個(gè)塊中,一般占用2KB。
-
超級(jí)塊:超級(jí)塊用于存儲(chǔ)一個(gè)已安裝的
文件系統(tǒng)全局配置參數(shù)(例如:塊大小,總的塊數(shù)和inode)和動(dòng)態(tài)信息(例如:當(dāng)前空閑塊數(shù)和inode數(shù)),其處于文件系統(tǒng)開(kāi)始位置的1K處,所占大小為1KB,為了系統(tǒng)的健壯性,最初每個(gè)塊組都有超級(jí)塊和組描述符表(GDT)一個(gè)拷貝,但是當(dāng)文件很大時(shí),這樣浪費(fèi)很多塊(尤其是GDT占用的塊多),后來(lái)采用了一種稀疏的方式存儲(chǔ)這些拷貝,只有快組號(hào)是3,5,7的冪的塊組才拷貝這個(gè)拷貝。通常情況下,只有主拷貝(第0塊塊組)的超級(jí)塊信息被文件系統(tǒng)使用,其他拷貝在文件系統(tǒng)被破壞的情況下才使用。
2.1 塊組描述符:GDT用于存儲(chǔ)塊組描述符,其占用一個(gè)或者多個(gè)數(shù)據(jù)塊,具體取決于文件系統(tǒng)的大小。它主要包含塊位圖,inode位圖和inode表位置,當(dāng)前空閑塊數(shù),inode數(shù)以及使用的目錄數(shù)。
2.2 塊組:(管理系統(tǒng)文件的塊、inode分配和管理的狀態(tài))每個(gè)塊組包含一個(gè)塊位圖,一個(gè)inode位塊圖,一個(gè)或多個(gè)用于描述inode表和用于存儲(chǔ)文件數(shù)據(jù)的數(shù)據(jù)塊。除此之外,還有可能包含超級(jí)塊和所有塊組描述符(取決于塊組號(hào)和文件系統(tǒng)創(chuàng)建時(shí)使用的參數(shù))。- 塊位圖: 用于描述該塊組所管理的塊的分配狀態(tài),如果某個(gè)塊對(duì)應(yīng)的位未置位,那么代表該塊未分配,可以用于存儲(chǔ)數(shù)據(jù);否則,代表該塊已經(jīng)存儲(chǔ)數(shù)據(jù)或者該塊不能夠使用。
- Inode位圖:用于描述該塊組所管理的inode的分配狀態(tài)。我們知道inode是用于描述文件的元數(shù)據(jù),每個(gè)inode對(duì)應(yīng)文件系統(tǒng)中唯一的一個(gè)號(hào),如果inode位圖中相應(yīng)位置位,那么代表該inode已經(jīng)分配出去,否則可以使用,由于其僅占一個(gè)塊,因此這也限制了一個(gè)塊組所能夠使用的最大inode數(shù)量。
-
inode表:用于存儲(chǔ)inode信息,占用一個(gè)或多個(gè)塊(為了有效利用空間,多個(gè)inode存儲(chǔ)在一個(gè)塊中),其大小取決于文件系統(tǒng)創(chuàng)建時(shí)的參數(shù),由于
inode位圖的限制,決定了其最大所占用的空間。
以上這幾個(gè)構(gòu)成了元素所在磁盤(pán)塊成為文件系統(tǒng)的元數(shù)據(jù)塊,剩余的部分則用來(lái)存儲(chǔ)真正的文件內(nèi)容,稱(chēng)為數(shù)據(jù)塊
block是實(shí)際文件的內(nèi)容,如果文件大于一個(gè)塊的時(shí)候,將占用多個(gè)block,但是一個(gè)塊只能存放一個(gè)文件(因?yàn)閿?shù)據(jù)是由inode指向的,如果兩個(gè)文件數(shù)據(jù)存放在一個(gè)塊里,就不能指定)
2.3 操作系統(tǒng)讀取文件

大體過(guò)程:
- 在讀取一個(gè)文件時(shí),總是從根目錄
(/)開(kāi)始讀取,每一個(gè)目錄或者文件,在VFS中,都是一個(gè)文件對(duì)象,每個(gè)文件對(duì)象都有唯一的一個(gè)inode與之對(duì)應(yīng)。 - 讀取到第一個(gè)
inode就是根目錄,讀取到了該目錄后,內(nèi)核對(duì)象就會(huì)為該對(duì)象建立一個(gè)目錄項(xiàng)對(duì)象(dentry),并將其緩存起來(lái),方便下一次讀取時(shí)直接從內(nèi)存中取。 - 目錄本身也是一個(gè)文件,目錄文件的內(nèi)容即是該目錄下的文件的名字與inode號(hào),目錄文件的內(nèi)容就像一張表,記錄的文件名和其inode之間的映射關(guān)系。根據(jù)路徑即可找到當(dāng)前需要讀取的下一級(jí)文件的名字和inode。同時(shí)繼續(xù)為該文件建立dentry。
- dentry結(jié)構(gòu)是一種含有指向父節(jié)點(diǎn)和子節(jié)點(diǎn)指針的雙向結(jié)構(gòu),多個(gè)這樣雙向結(jié)構(gòu)構(gòu)成了一個(gè)內(nèi)存里面的樹(shù)狀結(jié)構(gòu),也就是文件系統(tǒng)的目錄結(jié)構(gòu)在內(nèi)存中的緩存。
一個(gè)文件只有一個(gè)inode節(jié)點(diǎn)存放它的屬性信息,那么如果是一個(gè)大文件,那么他的block一定是多個(gè),且可能是不連續(xù)的,那么inode怎么表示呢?

如果文件內(nèi)容太大,對(duì)應(yīng)數(shù)據(jù)庫(kù)數(shù)量過(guò)多,inode節(jié)點(diǎn)本身提供的存儲(chǔ)空間不夠,會(huì)使用其他間接數(shù)據(jù)塊存儲(chǔ)數(shù)據(jù)塊位置信息,最多可以有三級(jí)尋址地址。
1、文件拷貝、剪切的底層過(guò)程是怎樣的?
2、軟連接和硬連接分別是如何實(shí)現(xiàn)的?
- 復(fù)制文件:創(chuàng)建一個(gè)新的inode節(jié)點(diǎn),并拷貝數(shù)據(jù)塊的內(nèi)容

-
移動(dòng)文件:同一個(gè)分區(qū)里面移動(dòng)文件
(mv),inode節(jié)點(diǎn)不變,只是更新目錄文件對(duì)應(yīng)數(shù)據(jù)塊里面的文件名和inode對(duì)應(yīng)關(guān)系。跨分區(qū),需要?jiǎng)?chuàng)建新的inode,因?yàn)閕node節(jié)點(diǎn)不同分區(qū)是不能共享的。


- 軟連接:創(chuàng)建軟連接會(huì)創(chuàng)建一個(gè)新的inode節(jié)點(diǎn),其對(duì)應(yīng)的數(shù)據(jù)塊內(nèi)容存儲(chǔ)所鏈接的文件名信息,這樣源文件即便刪除了,重新建立一個(gè)同名的文件,軟連接依然能夠生效。

-
硬連接:創(chuàng)建硬連接,并不會(huì)新建
inode節(jié)點(diǎn),只是links加一,然后在目錄文件對(duì)應(yīng)的數(shù)據(jù)塊上增減一條文件名和inode對(duì)應(yīng)的關(guān)系記錄。只有將硬鏈接和原文件都刪除之后,文件才會(huì)真正的刪除,即links為0才是真正的刪除。
推薦閱讀以及參考: