Shell 文件系統(tǒng)操作

2018-10-26 16:47 更新

前言

準(zhǔn)備了很久,找了好多天資料,還不知道應(yīng)該如何動(dòng)筆寫:因?yàn)閾?dān)心拿捏不住,所以一方面繼續(xù)查找資料,一方面思考如何來寫。作為《Shell編程范例》的一部分,希望它能夠很好地幫助 Shell 程序員理解如何用 Shell 命令來完成和 Linux 系統(tǒng)關(guān)系非常大的文件系統(tǒng)的各種操作,希望讓 Shell 程序員中對(duì)文件系統(tǒng)"混沌"的狀態(tài)從此消失,希望文件系統(tǒng)以一種更為清晰的樣子呈現(xiàn)在眼前。

文件系統(tǒng)在 Linux 操作系統(tǒng)中的位置

如何來認(rèn)識(shí)文件系統(tǒng)呢?從 Shell 程序員的角度來看,文件系統(tǒng)就是一個(gè)用來組織各種文件的方法。但是文件系統(tǒng)無法獨(dú)立于硬件存儲(chǔ)設(shè)備和操作系統(tǒng)而存在,因此還是有必要來弄清楚硬件存儲(chǔ)設(shè)備、分區(qū)、操作系統(tǒng)、邏輯卷、文件系統(tǒng)等各種概念之間的聯(lián)系,以便理解文件系統(tǒng)常規(guī)操作的一些“細(xì)節(jié)”。這個(gè)聯(lián)系或許(也許會(huì)有一些問題)可以通過這樣一種方式來呈現(xiàn):

Linux FileSystem Architecture

從圖中可以清晰地看到各個(gè)“概念”之間的關(guān)系,它們以不同層次分布,覆蓋硬件設(shè)備、系統(tǒng)內(nèi)核空間、系統(tǒng)用戶空間。在用戶空間,用戶可以不管內(nèi)核如何操作具體硬件設(shè)備,僅僅使用程序員設(shè)計(jì)的各種界面就可以,而普通程序員也僅僅需要利用內(nèi)核提供的各種接口(System Call)或者一些C庫來和內(nèi)核進(jìn)行交互,而無須關(guān)心具體的實(shí)現(xiàn)細(xì)節(jié)。不過對(duì)于操作系統(tǒng)開發(fā)人員,他們需要在內(nèi)核空間設(shè)計(jì)特定的數(shù)據(jù)結(jié)構(gòu)來管理和組織底層的硬件設(shè)備。

下面從下到上的方式(即從底層硬件開始),用工具來分析和理解圖中幾個(gè)重要概念。(如果有興趣,可以先看看下面的幾則資料)

參考資料:

硬件管理和設(shè)備驅(qū)動(dòng)

Linux 系統(tǒng)通過設(shè)備驅(qū)動(dòng)管理硬件設(shè)備。如果添加了新的硬件設(shè)備,那么需要編寫相應(yīng)的硬件驅(qū)動(dòng)來管理它。對(duì)于一些常見的硬件設(shè)備,系統(tǒng)已經(jīng)自帶了相應(yīng)的驅(qū)動(dòng),編譯內(nèi)核時(shí),選中它們,然后編譯成內(nèi)核的一部分或者以模塊的方式編譯。如果以模塊的方式編譯,那么可以在系統(tǒng)的 /lib/modules/$(uname -r)目錄下找到對(duì)應(yīng)的模塊文件。

范例:查找設(shè)備所需的驅(qū)動(dòng)文件

比如,可以這樣找到相應(yīng)的 scsi 驅(qū)動(dòng)和 usb 驅(qū)動(dòng)對(duì)應(yīng)的模塊文件:

更新系統(tǒng)中文件索引數(shù)據(jù)庫(有點(diǎn)慢)

$ updatedb

查找 scsi 相關(guān)的驅(qū)動(dòng)

$ locate scsi*.ko

查找 usb 相關(guān)的驅(qū)動(dòng)

$ locate usb*.ko

這些驅(qū)動(dòng)以 .ko 為后綴,在安裝系統(tǒng)時(shí)默認(rèn)編譯為了模塊。實(shí)際上可以把它們編譯為內(nèi)核的一部分,僅僅需要在編譯內(nèi)核時(shí)選擇為[*]即可。但是,很多情況下會(huì)以模塊的方式編譯它們,這樣可以減少內(nèi)核的大小,并根據(jù)需要靈活地加載和卸載它們。下面簡(jiǎn)單地演示如何卸載模塊、加載模塊以及查看已加載模塊的狀態(tài)。

可通過 /proc 文件系統(tǒng)的 modules 文件檢查內(nèi)核中已加載的各個(gè)模塊的狀態(tài),也可以通過 lsmod 命令直接查看它們。

$ cat /proc/modules

或者

$ lsmod

范例:查看已經(jīng)加載的設(shè)備驅(qū)動(dòng)

查看 scsi 和 usb 相關(guān)驅(qū)動(dòng),結(jié)果各列為模塊名、模塊大小、被其他模塊的引用情況(引用次數(shù)、引用它們的模塊)

$ lsmod | egrep "scsi|usb"
usbhid                 29536  0
hid                    28928  1 usbhid
usbcore               138632  4 usbhid,ehci_hcd,ohci_hcd
scsi_mod              147084  4 sg,sr_mod,sd_mod,libata

范例:卸載設(shè)備驅(qū)動(dòng)

下面卸載 usbhid 模塊看看(不要卸載scsi的驅(qū)動(dòng)!因?yàn)槟愕南到y(tǒng)可能就跑在上面,如果確實(shí)想玩玩,卸載前記得保存數(shù)據(jù)),通過 rmmod 命令就可以實(shí)現(xiàn),先切換到 Root 用戶:

$ sudo -s
# rmmod usbhid

再查看該模塊的信息,已經(jīng)看不到了吧

$ lsmod | grep ^usbhid

范例:掛載設(shè)備驅(qū)動(dòng)

如果有個(gè) usb 鼠標(biāo),那么移動(dòng)一下,是不是發(fā)現(xiàn)動(dòng)不了啦?因?yàn)樵O(shè)備驅(qū)動(dòng)都沒有了,設(shè)備自然就沒法用羅。不過不要緊張,既然知道原因,那么重新加載驅(qū)動(dòng)就可以,下面用 insmodusbhid 模塊重新加載上。

$ sudo -s
# insmod `locate usbhid.ko`

locate usbhid.ko 是為了找出 usbhid.ko 模塊的路徑,如果之前沒有 updatedb,估計(jì)用它是找不到了,不過也可以直接到 /lib/modules 目錄下用 findusbhid.ko 文件找到。

# insmod $(find /lib/modules -name "*usbhid.ko*" | grep `uname -r`)

現(xiàn)在鼠標(biāo)又可以用啦,不信再動(dòng)一下鼠標(biāo) :-)

到這里,硬件設(shè)備和設(shè)備驅(qū)動(dòng)之間關(guān)系應(yīng)該是比較清楚了。如果沒有,那么繼續(xù)下面的內(nèi)容。

范例:查看設(shè)備驅(qū)動(dòng)對(duì)應(yīng)的設(shè)備文件

Linux 設(shè)備驅(qū)動(dòng)關(guān)聯(lián)著相應(yīng)的設(shè)備文件,而設(shè)備文件則和硬件設(shè)備一一對(duì)應(yīng)。這些設(shè)備文件都統(tǒng)一存放在系統(tǒng)的 /dev/ 目錄下。

例如,scsi 設(shè)備對(duì)應(yīng)/dev/sda,/dev/sda1,/dev/sda2... 下面查看這些設(shè)備信息。

$ ls -l /dev/sda*
brw-rw---- 1 root disk 8, 0 2007-12-28 22:49 /dev/sda
brw-rw---- 1 root disk 8, 1 2007-12-28 22:50 /dev/sda1
brw-rw---- 1 root disk 8, 3 2007-12-28 22:49 /dev/sda3
brw-rw---- 1 root disk 8, 4 2007-12-28 22:49 /dev/sda4
brw-rw---- 1 root disk 8, 5 2007-12-28 22:50 /dev/sda5
brw-rw---- 1 root disk 8, 6 2007-12-28 22:50 /dev/sda6
brw-rw---- 1 root disk 8, 7 2007-12-28 22:50 /dev/sda7
brw-rw---- 1 root disk 8, 8 2007-12-28 22:50 /dev/sda8

可以看到第一列第一個(gè)字符都是 b,第五列都是數(shù)字 8 。 b 表示該文件是一個(gè)塊設(shè)備文件,對(duì)應(yīng)地,如果是 c 則表示字符設(shè)備(例如 `/dev/ttyS0),關(guān)于塊設(shè)備和字符設(shè)備的區(qū)別,可以看這里:

  • 字符設(shè)備:字符設(shè)備就是能夠像字節(jié)流一樣訪問的設(shè)備,字符終端和串口就屬于字符設(shè)備。

  • 塊設(shè)備:塊設(shè)備上可以容納文件系統(tǒng)。與字符設(shè)備不同,在讀寫時(shí),塊設(shè)備每次只能傳輸一個(gè)或多個(gè)完整的塊。在 Linux 操作系統(tǒng)中,應(yīng)用程序可以像訪問字符設(shè)備一樣讀寫塊設(shè)備(一次讀取或?qū)懭肴我獾淖止?jié)數(shù)據(jù))。因此,塊設(shè)備和字符設(shè)備的區(qū)別僅僅是在內(nèi)核中對(duì)于數(shù)據(jù)的管理不同。

數(shù)字 8 則是該硬件設(shè)備在內(nèi)核中對(duì)應(yīng)的設(shè)備編號(hào),可以在內(nèi)核的 Documentation/devices.txt/proc/devices 文件中找到設(shè)備號(hào)分配情況。但是為什么同一個(gè)設(shè)備會(huì)對(duì)應(yīng)不同的設(shè)備文件(/dev/sda 后面為什么還有不同的數(shù)字,而且 ls 結(jié)果中的第 6 列和它們對(duì)應(yīng)起來)。這實(shí)際上是為了區(qū)分不同設(shè)備的不同部分。對(duì)于硬盤,這樣可以處理硬盤內(nèi)部的不同分區(qū)。就內(nèi)核而言,它僅僅需要通過第 5 列的設(shè)備號(hào)就可以找到對(duì)應(yīng)的硬件設(shè)備,但是對(duì)于驅(qū)動(dòng)模塊來說,它還需要知道如何處理不同的分區(qū),于是就多了一個(gè)輔設(shè)備號(hào),即第 6 列對(duì)應(yīng)的內(nèi)容。這樣一個(gè)設(shè)備就有了主設(shè)備號(hào)(第 5 列)和輔設(shè)備號(hào)(第 6 列),從而方便地實(shí)現(xiàn)對(duì)各種硬件設(shè)備的管理。

因?yàn)樵O(shè)備文件和硬件是對(duì)應(yīng)的,這樣可以直接從 /dev/sda (如果是 IDE 的硬盤,那么對(duì)應(yīng)的設(shè)備就是 /dev/hda 啦)設(shè)備中讀出硬盤的信息,例如:

范例:訪問設(shè)備文件

dd 命令復(fù)制出硬盤的前 512 個(gè)字節(jié),要 Root 用戶

$ sudo dd if=/dev/sda of=mbr.bin bs=512 count=1

file 命令查看相應(yīng)的信息

$ file mbr.bin
mbr.bin: x86 boot sector, LInux i386 boot LOader; partition 3: ID=0x82, starthead 254, startsector 19535040, 1959930 sectors; partition 4: ID=0x5, starthead 254, startsector 21494970, 56661255 sectors, code offset 0x48

也可以用 od 命令以 16 進(jìn)制的形式讀取并進(jìn)行分析

$ od -x mbr.bin

bs 是塊的大?。ㄒ宰止?jié) bytes 為單位),count 是塊數(shù)

因?yàn)檫@些信息并不直觀(而且下面會(huì)進(jìn)一步深入分析),那么先來看看另外一個(gè)設(shè)備文件,將可以非常直觀地演示設(shè)備文件和硬件的對(duì)應(yīng)關(guān)系。還是以鼠標(biāo)為例吧,下面來讀取鼠標(biāo)對(duì)應(yīng)的設(shè)備文件的信息。

$ sudo -s
# cat /dev/input/mouse1 | od -x

你的鼠標(biāo)驅(qū)動(dòng)可能不太一樣,所以設(shè)備文件可能是其他的,但是都會(huì)在 /dev/input 下。

移動(dòng)鼠標(biāo)看看,是不是發(fā)現(xiàn)有不同信息輸出。基于這一原理,我們經(jīng)常通過在一端讀取設(shè)備文件 /dev/ttyS0 中的內(nèi)容,而在另一端往設(shè)備文件 /dev/ttyS0 中寫入內(nèi)容來檢查串口線是否被損壞。

到這里,對(duì)設(shè)備驅(qū)動(dòng)、設(shè)備文件和硬件設(shè)備之間的關(guān)聯(lián)應(yīng)該是印象更深刻了。如果想深入了解設(shè)備驅(qū)動(dòng)的工作原理和設(shè)備驅(qū)動(dòng)的編寫,那么看看下面列出的相關(guān)資料,開始設(shè)備驅(qū)動(dòng)的編寫歷程吧。

參考資料:

理解、查看磁盤分區(qū)

實(shí)際上內(nèi)存、u 盤等都可以作為文件系統(tǒng)底層的“存儲(chǔ)”設(shè)備,但是這里僅用硬盤作為實(shí)例來介紹磁盤和分區(qū)的關(guān)系。

目前 Linux 的分區(qū)依然采用第一臺(tái)PC硬盤所使用的分區(qū)原理,下面逐步分析和演示這一分區(qū)原理。

磁盤分區(qū)基本原理

先來看看幾個(gè)概念:

  • 設(shè)備管理和分區(qū)

Linux 下,每一個(gè)存儲(chǔ)設(shè)備對(duì)應(yīng)一個(gè)系統(tǒng)的設(shè)備文件,對(duì)于硬盤等 IDESCSI 設(shè)備,在系統(tǒng)的 /dev 目錄下可以找到對(duì)應(yīng)的包含字符 hdsd 的設(shè)備文件。而根據(jù)硬盤連接的主板設(shè)備接口和數(shù)據(jù)線接口的不同,在 hd 或者 sd 字符后面可以添加一個(gè)從 az 的字符,例如 hda,hdb,hdcsda,sdb,sdc 等,另外為了區(qū)別同一個(gè)硬件設(shè)備的不同分區(qū),在后面還可以添加了一個(gè)數(shù)字,例如 hda1,hda2hda3sda1,sda2sda3,所以在 /dev 目錄下,可以看到很多類似的設(shè)備文件。

  • 各分區(qū)的作用

在分區(qū)時(shí)常遇到主分區(qū)和邏輯分區(qū)的問題,這實(shí)際上是為了方便擴(kuò)展分區(qū),正如后面的邏輯卷的引入是為了更好地管理多個(gè)硬盤一樣,引入主分區(qū)和邏輯分區(qū)可以方便地進(jìn)行分區(qū)的管理。

Linux 系統(tǒng)中每一個(gè)硬盤設(shè)備最多由 4 個(gè)主分區(qū)(包括擴(kuò)展分區(qū))構(gòu)成。

主分區(qū)的作用是計(jì)算機(jī)用來進(jìn)行啟動(dòng)操作系統(tǒng)的,因此每一個(gè)操作系統(tǒng)的啟動(dòng)程序或者稱作是引導(dǎo)程序,都應(yīng)該存放在主分區(qū)上。 Linux 規(guī)定主分區(qū)(或者擴(kuò)展分區(qū))占用分區(qū)編號(hào)中的前 4 個(gè)。所以會(huì)看到主分區(qū)對(duì)應(yīng)的設(shè)備文件為 /dev/hda1-4 或者 /dev/sda1-4,而不會(huì)是 hda5 或者 sda5 。

擴(kuò)展分區(qū)則是為了擴(kuò)展更多的邏輯分區(qū)的,在 Linux 下,邏輯分區(qū)占用了 hda5-16 或者 sda5-16 等 12 個(gè)編號(hào)。

  • 分區(qū)類型

它規(guī)定了這個(gè)分區(qū)上的文件系統(tǒng)的類型。Linux支持諸如msdoc,vfat,ext2,ext3等諸多的文件系統(tǒng)類型,更多信息在下一小節(jié)進(jìn)行進(jìn)一步的介紹。

通過分析 MBR 來理解分區(qū)原理

下面通過分析硬盤的前 512 個(gè)字節(jié)(即 MBR)來分析和理解分區(qū)。

先來看看這張圖:

MBR Architecture

它用來描述 MBR 的結(jié)構(gòu)。 MBR 包括引導(dǎo)部分、分區(qū)表、以及結(jié)束標(biāo)記 `(55AAH),分別占用了 512 字節(jié)中 446 字節(jié)、 64 字節(jié)和 2 字節(jié)。這里僅僅關(guān)注分區(qū)表部分,即中間的 64 字節(jié)以及圖中左邊的部分。

由于我用的是 SCSI 的硬盤,下面從 /dev/sda 設(shè)備中把硬盤的前 512 個(gè)字節(jié)拷貝到文件 mbr.bin 中。

$ sudo -s
# dd if=/dev/sda of=mbr.bin bs=512 count=1

下面用 file,od,fdisk 等命令來分析這段 MBR 的數(shù)據(jù),并對(duì)照上圖以便加深理解。

$ file mbr.bin
mbr.bin: x86 boot sector, LInux i386 boot LOader; partition 3: ID=0x82, starthead 254, startsector 19535040, 1959930 sectors; partition 4: ID=0x5, starthead 254, startsector 21494970, 56661255 sectors, code offset 0x48
$ od -x mbr.bin | tail -6   #僅關(guān)注中間的64字節(jié),所以截取了結(jié)果中后6行
0000660 0000 0000 0000 0000 a666 a666 0000 0180
0000700 0001 fe83 ffff 003f 0000 1481 012a 0000
0000720 0000 0000 0000 0000 0000 0000 0000 fe00
0000740 ffff fe82 ffff 14c0 012a e7fa 001d fe00
0000760 ffff fe05 ffff fcba 0147 9507 0360 aa55
$ sudo -s
# fdisk -l | grep ^/  #僅分析MBR相關(guān)的部分,不分析邏輯分區(qū)部分
/dev/sda1   *           1        1216     9767488+  83  Linux
/dev/sda3            1217        1338      979965   82  Linux swap / Solaris
/dev/sda4            1339        4865    28330627+   5  Extended

file 命令的結(jié)果顯示,剛拷貝的 512 字節(jié)是啟動(dòng)扇區(qū),用分號(hào)分開的幾個(gè)部分分別是 bootloader,分區(qū) 3 和分區(qū) 4 。分區(qū) 3 的類型是 82,即 swap 分區(qū)(可以通過 fdisk 命令的 l 命令列出相關(guān)信息),它對(duì)應(yīng) fdisk 的結(jié)果中 /dev/sda3 所在行的第 5 列,分區(qū) 3 的扇區(qū)數(shù)是 1959930,轉(zhuǎn)換成字節(jié)數(shù)是 1959930\*512 (目前,硬盤的默認(rèn)扇區(qū)大小是 512 字節(jié)),而 swap 分區(qū)的默認(rèn)塊大小是 1024 字節(jié),這樣塊數(shù)就是 :

$ echo 1959930*512/1024 | bc
979965

正好是 fdisk 結(jié)果中 /dev/sda3 所在行的第四列對(duì)應(yīng)的塊數(shù),同樣地,可以對(duì)照 fdiskfile 的結(jié)果分析分區(qū) 4 。

再來看看 od 命令以十六進(jìn)制顯示的結(jié)果,同樣考慮分區(qū) 3,計(jì)算一下發(fā)現(xiàn),分區(qū) 3 對(duì)應(yīng)的 od 命令的結(jié)果為:

fe00 ffff fe82 ffff 14c0 012a e7fa 001d

首先是分區(qū)標(biāo)記,00H,從上圖中,看出它就不是引導(dǎo)分區(qū)(80H 標(biāo)記的才是引導(dǎo)分區(qū)),而分區(qū)類型呢?為 82H,和 file 顯示結(jié)果一致,現(xiàn)在再來關(guān)注一下分區(qū)大小,即 file 結(jié)果中的扇區(qū)數(shù)。

$ echo "ibase=10;obase=16;1959930" | bc
1DE7FA

剛好對(duì)應(yīng) e7fa 001d,同樣地考慮引導(dǎo)分區(qū)的結(jié)果:

0180 0001 fe83 ffff 003f 0000 1481 012a

分區(qū)標(biāo)記: 80H,正好反應(yīng)了這個(gè)分區(qū)是引導(dǎo)分區(qū),隨后是引導(dǎo)分區(qū)所在的磁盤扇區(qū)情況,010100,即 1 面 0 道 1 扇區(qū)。其他內(nèi)容可以對(duì)照分析。

考慮到時(shí)間關(guān)系,更多細(xì)節(jié)請(qǐng)參考下面的資料或者查看看系統(tǒng)的相關(guān)手冊(cè)。

補(bǔ)充:安裝系統(tǒng)時(shí),可以用 fdisk,cfdisk 等命令進(jìn)行分區(qū)。如果要想從某個(gè)分區(qū)啟動(dòng),那么需要打上 80H 標(biāo)記,例如可通過 cfdisk 把某個(gè)分區(qū)設(shè)置為 bootable 來實(shí)現(xiàn)。

參考資料:

分區(qū)和文件系統(tǒng)的關(guān)系

在沒有引入邏輯卷之前,分區(qū)類型和文件系統(tǒng)類型幾乎可以同等對(duì)待,設(shè)置分區(qū)類型的過程就是格式化分區(qū),建立相應(yīng)的文件系統(tǒng)類型的過程。

下面主要介紹如何建立分區(qū)和文件系統(tǒng)類型的聯(lián)系,即如何格式化分區(qū)為指定的文件系統(tǒng)類型。

常見分區(qū)類型

先來看看 Linux 下文件系統(tǒng)的常見類型(如果要查看所有 Linux 支持的文件類型,可以用 fdisk 命令的 l 命令查看,或者通過 man fs 查看,也可通過 /proc/filesystems 查看到當(dāng)前內(nèi)核支持的文件系統(tǒng)類型)

  • ext2,ext3,ext4 :這三個(gè)是 Linux 根文件系統(tǒng)通常采用的類型
  • swap :這個(gè)是實(shí)現(xiàn) Linux 虛擬內(nèi)存時(shí)采用的一種文件系統(tǒng),安裝時(shí)一般需要建立一個(gè)專門的分區(qū),并格式化為 swap 文件系統(tǒng)(如果想添加更多 swap 分區(qū),可以參考本節(jié)的參考資料,熟悉 ddmkswap,swapon,swapoff 等命令的用法)
  • proc :這是一種比較特別的文件系統(tǒng),作為內(nèi)核和用戶之間的一個(gè)接口存在,建立在內(nèi)存中(可以通過 cat 命令查看 /proc 系統(tǒng)下的文件,甚至可以通過修改 /proc/sys 下的文件實(shí)時(shí)調(diào)整內(nèi)核配置,當(dāng)前前提是需要把 proc 文件系統(tǒng)掛載上: mount -t proc proc /proc

除了上述文件系統(tǒng)類型外,Linux 支持包括 vfatiso,xfsnfs 在內(nèi)各種常見的文件系統(tǒng)類型,在 Linux 下,可以自由地查看和操作 Windows 等其他操作系統(tǒng)使用的文件系統(tǒng)。

那么如何建立磁盤和這些文件系統(tǒng)類型的關(guān)聯(lián)呢?格式化。

格式化的過程實(shí)際上就是重新組織分區(qū)的過程,可通過 mkfs 命令來實(shí)現(xiàn),當(dāng)然也可以通過 fdisk 等命令來實(shí)現(xiàn)。這里僅介紹 mkfsmkfs 可用來對(duì)一個(gè)已有的分區(qū)進(jìn)行格式化,不能實(shí)現(xiàn)分區(qū)操作(如果要對(duì)一個(gè)磁盤進(jìn)行分區(qū)和格式化,那么可以用 fdisk)。格式化后,相應(yīng)分區(qū)上的數(shù)據(jù)就會(huì)通過某種特別的文件系統(tǒng)類型進(jìn)行組織。

范例:格式化文件系統(tǒng)

例如:把 /dev/sda9 分區(qū)格式化為 ext3 的文件系統(tǒng)。

$ sudo -s
# mkfs -t ext3 /dev/sda9

如果要列出各個(gè)分區(qū)的文件系統(tǒng)類型,那么可以用 fdisk -l 命令。

更多信息請(qǐng)參考下列資料。

參考資料:

分區(qū)、邏輯卷和文件系統(tǒng)的關(guān)系

上一節(jié)直接把分區(qū)格式化為某種文件系統(tǒng)類型,但是考慮到擴(kuò)展新的存儲(chǔ)設(shè)備的需要,開發(fā)人員在文件系統(tǒng)和分區(qū)之間引入了邏輯卷??紤]到時(shí)間關(guān)系,這里不再詳述,請(qǐng)參考資料:Linux 邏輯卷管理詳解

文件系統(tǒng)的可視化結(jié)構(gòu)

文件系統(tǒng)最終呈現(xiàn)出來的是一種可視化的結(jié)構(gòu),可用ls,find,tree等命令把它呈現(xiàn)出來。它就像一顆倒掛的“樹”,在樹的節(jié)點(diǎn)上還可以掛載新的“樹”。

下面簡(jiǎn)單介紹文件系統(tǒng)的掛載。

一個(gè)文件系統(tǒng)可以通過一個(gè)設(shè)備掛載(mount)到某個(gè)目錄下,這個(gè)目錄被稱為掛載點(diǎn)。有趣的是,在 Linux 下,一個(gè)目錄本身還可以掛載到另外一個(gè)目錄下,一個(gè)格式化了的文件也可以通過一個(gè)特殊的設(shè)備 /dev/loop 進(jìn)行掛載(如 iso 文件)。另外,就文件系統(tǒng)而言,Linux 不僅支持本地文件系統(tǒng),還支持遠(yuǎn)程文件系統(tǒng)(如 nfs)。

范例:掛載文件系統(tǒng)

下面簡(jiǎn)單介紹文件系統(tǒng)掛載的幾個(gè)實(shí)例。

  • 根文件系統(tǒng)的掛載

掛載需要 Root 權(quán)限,例如,掛載系統(tǒng)根文件系統(tǒng) /dev/sda1/mnt

$ sudo -s
# mount -t ext3 /dev/sda1 /mnt/

查看 /dev/sda1 的掛載情況,可以看到,一個(gè)設(shè)備可以多次掛載

$ mount | grep sda1
/dev/sda1 on / type ext3 (rw,errors=remount-ro)
/dev/sda1 on /mnt type ext3 (rw)

對(duì)于一個(gè)已經(jīng)掛載的文件系統(tǒng),為支持不同屬性可以重新掛載

$ mount -n -o remount, rw /
  • 掛載一個(gè)新增設(shè)備

如果內(nèi)核已經(jīng)支持 USB 接口,那么插入 u 盤時(shí),可以通過 dmesg 命令查看對(duì)應(yīng)的設(shè)備號(hào),并掛載它。

查看 dmesg 結(jié)果中的最后幾行內(nèi)容,找到類似 /dev/sdN 的信息,找出 u 盤對(duì)應(yīng)的設(shè)備號(hào)

$ dmesg

這里假設(shè) u 盤是 vfat 格式,以便在一些打印店里的 Windows 上也可使用

# mount -t vfat /dev/sdN /path/to/mountpoint_directory
  • 掛載一個(gè) iso 文件或者是光盤

對(duì)于一些iso文件或者是 iso 格式的光盤,同樣可以通過 mount 命令掛載。

對(duì)于 iso 文件:

# mount -t iso9660 /path/to/isofile /path/to/mountpoint_directory

對(duì)于光盤:

# mount -t iso9660 /dev/cdrom /path/to/mountpoint_directory
  • 掛載一個(gè)遠(yuǎn)程文件系統(tǒng)
# mount -t nfs remote_ip:/path/to/share_directory /path/to/local_directory
  • 掛載一個(gè) proc 文件系統(tǒng)
# mount -t proc proc /proc

proc 文件系統(tǒng)組織在內(nèi)存中,但是可以把它掛載到某個(gè)目錄下。通常把它掛載在 /proc 目錄下,以便一些系統(tǒng)管理和配置工具使用它。例如 top 命令用它分析內(nèi)存的使用情況(讀取 /proc/meminfo/proc/stat 等文件中的內(nèi)容); lsmod 命令通過它獲取內(nèi)核模塊的狀態(tài)(讀取 /proc/modules); netstat 命令通過它獲取網(wǎng)絡(luò)的狀態(tài)(讀取 /proc/net/dev 等文件)。當(dāng)然,也可以編寫相關(guān)工具。除此之外,通過調(diào)整 /proc/sys 目錄下的文件,可以動(dòng)態(tài)地調(diào)整系統(tǒng)配置,比如往 /proc/sys/net/ipv4/ip_forward 文件中寫入數(shù)字 1 就可以讓內(nèi)核支持?jǐn)?shù)據(jù)包轉(zhuǎn)發(fā)。(更多信息請(qǐng)參考 proc 的幫助,man``proc

  • 掛載一個(gè)目錄
$ mount --bind /path/to/needtomount_directory /path/to/mountpoint_directory

這個(gè)非常有意思,比如可以把某個(gè)目錄掛載到 ftp 服務(wù)的根目錄下,而無須把內(nèi)容復(fù)制過去,就可以把相應(yīng)目錄中的資源提供給別人共享。

范例:卸載某個(gè)分區(qū)

以上都只提到了掛載,那怎么卸載呢?用 umount 命令跟上掛載的源地址或者掛載點(diǎn)(設(shè)備,文件,遠(yuǎn)程目錄等)就可以。例如:

$ umount /path/to/mountpoint_directory

或者

$ umount /path/to/mount_source

如果想管理大量的或者經(jīng)常性的掛載服務(wù),那么每次手動(dòng)掛載是很糟糕的事情。這時(shí)就可利用 mount 的配置文件 /etc/fstab,把 mount 對(duì)應(yīng)的參數(shù)寫到 /etc/fstab 文件對(duì)應(yīng)的列中即可實(shí)現(xiàn)批量掛載( mount -a )和卸載( umount -a )。 /etc/fstab 中各列分別為文件系統(tǒng)、掛載點(diǎn)、類型、相關(guān)選項(xiàng)。更多信息可參考 fstab 的幫助( man fstab )。

參考資料:

如何制作一個(gè)文件系統(tǒng)

Linux 文件系統(tǒng)下有一些最基本的目錄,不同的目錄下存放著不同作用的各類文件。最基本的目錄有 /etc/lib,/dev,/bin 等,它們分別存放著系統(tǒng)配置文件,庫文件,設(shè)備文件和可執(zhí)行程序。這些目錄一般情況下是必須的,在做嵌入式開發(fā)時(shí),需要手動(dòng)或者是用 busybox 等工具來創(chuàng)建這樣一個(gè)基本的文件系統(tǒng)。這里僅制作一個(gè)非常簡(jiǎn)單的文件系統(tǒng),并對(duì)該文件系統(tǒng)進(jìn)行各種常規(guī)操作,以便加深對(duì)文件系統(tǒng)的理解。

范例:用 dd 創(chuàng)建一個(gè)固定大小的文件

還記得 dd 命令么?就用它來產(chǎn)生一個(gè)固定大小的文件,這個(gè)為 1M(1024\*1024 bytes) 的文件

$ dd if=/dev/zero of=minifs bs=1024 count=1024

查看文件類型,這里的 minifs 是一個(gè)充滿 \\0 的文件,沒有任何特定的數(shù)據(jù)結(jié)構(gòu)

$ file minifs
minifs: data

說明: /dev/zero 是一個(gè)非常特殊的設(shè)備,如果讀取它,可以獲取任意多個(gè) \\0 。

接著把該文件格式化為某個(gè)指定文件類型的文件系統(tǒng)。(是不是覺得不可思議,文件也可以格式化?是的,不光是設(shè)備可以,文件也可以以某種文件系統(tǒng)類型進(jìn)行組織,但是需要注意的是,某些文件系統(tǒng)(如 ext3)要求被格式化的目標(biāo)最少有 64M 的空間)。

范例:用 mkfs 格式化文件

$ mkfs.ext2 minifs

查看此時(shí)的文件類型,這時(shí)文件 minifs 就以 ext2 文件系統(tǒng)的格式組織了

$ file minifs
minifs: Linux rev 1.0 ext2 filesystem data

范例:掛載剛創(chuàng)建的文件系統(tǒng)

因?yàn)樵撐募晕募到y(tǒng)的類型組織了,那么可以用 mount 命令掛載并使用它。

請(qǐng)切換到 root 用戶掛載它,并通過 -o loop 選項(xiàng)把它關(guān)聯(lián)到一個(gè)特殊設(shè)備 /dev/loop

$ sudo -s
# mount minifs /mnt/ -o loop

查看該文件系統(tǒng)信息,僅可以看到一個(gè)目錄文件 lost+found

$ ls /mnt/
lost+found

范例:對(duì)文件系統(tǒng)進(jìn)行讀、寫、刪除等操作

在該文件系統(tǒng)下進(jìn)行各種常規(guī)操作,包括讀、寫、刪除等。(每次操作前先把 minifs 文件保存一份,以便比較,結(jié)合相關(guān)資料就可以深入地分析各種操作對(duì)文件系統(tǒng)的改變情況,從而深入理解文件系統(tǒng)作為一種組織數(shù)據(jù)的方式的實(shí)現(xiàn)原理等)

$ cp minifs minifs.bak
$ cd /mnt
$ touch hello
$ cd -
$ cp minifs minifs-touch.bak
$ od -x minifs.bak > orig.od
$ od -x minifs-touch.bak > touch.od

創(chuàng)建一個(gè)文件后,比較此時(shí)文件系統(tǒng)和之前文件系統(tǒng)的異同

$ diff orig.od touch.od
diff orig.od touch.od
61,63c61,64
< 0060020 000c 0202 2e2e 0000 000b 0000 03e8 020a
< 0060040 6f6c 7473 662b 756f 646e 0000 0000 0000
< 0060060 0000 0000 0000 0000 0000 0000 0000 0000
---
> 0060020 000c 0202 2e2e 0000 000b 0000 0014 020a
> 0060040 6f6c 7473 662b 756f 646e 0000 000c 0000
> 0060060 03d4 0105 6568 6c6c 006f 0000 0000 0000
> 0060100 0000 0000 0000 0000 0000 0000 0000 0000

通過比較發(fā)現(xiàn):添加文件,文件系統(tǒng)的相應(yīng)位置發(fā)生了明顯的變化

$ echo "hello, world" > /mnt/hello

執(zhí)行 sync 命令,確保緩存中的數(shù)據(jù)已經(jīng)寫入磁盤(還記得本節(jié)圖 1 的 buffer cache 吧,這里就是把 cache 中的數(shù)據(jù)寫到磁盤中)

$ sync
$ cp minifs minifs-echo.bak
$ od -x minifs-echo.bak > echo.od

寫入文件內(nèi)容后,比較文件系統(tǒng)和之前的異同

$ diff touch.od echo.od

查看文件系統(tǒng)中的字符串

$ strings minifs
lost+found
hello
hello, world

刪除 hello 文件,查看文件系統(tǒng)變化

$ rm /mnt/hello
$ cp minifs minifs-rm.bak
$ od -x minifs-rm.bak > rm.od
$ diff echo.od rm.od

通過查看文件系統(tǒng)的字符串發(fā)現(xiàn):刪除文件時(shí)并沒有覆蓋文件內(nèi)容,所以從理論上說內(nèi)容此時(shí)還是可恢復(fù)的

$ strings minifs
lost+found
hello
hello, world

上面僅僅演示了一些分析文件系統(tǒng)的常用工具,并分析了幾個(gè)常規(guī)的操作,如果想非常深入地理解文件系統(tǒng)的實(shí)現(xiàn)原理,請(qǐng)熟悉使用上述工具并閱讀相關(guān)資料。

參考資料:

如何開發(fā)自己的文件系統(tǒng)

隨著 fuse 的出現(xiàn),在用戶空間開發(fā)文件系統(tǒng)成為可能,如果想開發(fā)自己的文件系統(tǒng),那么推薦閱讀:使用 fuse 開發(fā)自己的文件系統(tǒng)

后記

  • 2007 年 12 月 22 日,收集了很多資料,寫了整體的框架
  • 2007 年 12 月 28 日下午,完成初稿,考慮到時(shí)間關(guān)系,很多細(xì)節(jié)也沒有進(jìn)一步分析,另外有些部分可能存在理解上的問題,歡迎批評(píng)指正
  • 2007 年 12 月 28 日晚,修改部分資料,并正式公開該篇文檔
  • 29 號(hào),添加設(shè)備驅(qū)動(dòng)和硬件設(shè)備一小節(jié)
以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)