shell編程范例之文件系統操作
圖片:

圖片:

by falcon<zhangjinw@gmail.com>
2007-12-20
前言
準備了很久,找了好多天的資料,還不知道應該如何開始動筆寫:因為擔心“拿捏”不住,所以一方面繼續查找資料,一方面思考如何來寫。作為“shell編程 范例”序列的一部分,希望它能夠很好地幫助shell程序員理解如何用shell命令來完成和Linux系統關系非常之大的文件系統的各種操作,希望讓 Shell程序員中對文件系統"混沌"的狀態從此消失,希望文件系統以一種更為清晰的樣子呈現在我們的眼前。
正文
-1 文件系統在Linux操作系統中的位置
如何來認識文件系統呢?從shell程序員的角度來看,文件系統就是一個用來組織各種文件的方法。但是文件系統無法獨立于硬件存儲設備和操作系統而獨立存 在,因此我們還是有必要來弄清楚硬件存儲設備、分區、操作系統、邏輯卷、文件系統等各種概念之間的聯系,以便理解我們對文件系統的常規操作的一些“細 節”。這個聯系或許(也許會有一些問題)可以通過這樣一種方式來呈現,如附錄圖[1]。
從該圖中,我們可以清晰地看到各個“概念”之間的關系,它們以不同層次分布,覆蓋硬件設備、系統內核空間、系統用戶空間。在用戶空間,用戶可以不管內核是 如何操作具體硬件設備的,僅僅使用程序員設計的各種界面就可以拉,而普通程序員也僅僅需要利用內核提供的各種接口(system call)或者一些C庫來和內核進行交互,而無須關心具體的實現細節。不過對于操作系統開發人員,他們需要在內核空間設計特定的數據結構來管理和組織底層 的硬件設備。
下面我們從下到上的方式(即從底層硬件開始),用工具來分析和理解圖中幾個重要的概念。(如果有興趣,可以先看看下面的幾則資料)
參考資料:
[1] Linux 系統的基本組成和文件系統結構
http://forum.ubuntu.org.cn/weblog_entry.php?e=332&sid=3ceee92718a77d5eef867497470ecc7b
[2] 從文件 I/O 看 Linux 的虛擬文件系統
http://www.ibm.com/developerworks/cn/linux/l-cn-vfs/
[3] Linux 文件系統剖析 http://www.ibm.com/developerworks/cn/linux/l-linux-filesystem/index.html?ca=drs-cn
[4] 第九章 文件系統
http://man.chinaunix.net/tech/lyceum/linuxK/fs/filesystem.html
[5] Linux邏輯盤卷管理LVM詳解
http://unix-cd.com/vc/www/28/2007-06/1178.html
0 硬件管理和設備驅動
Linux系統通過不同的設備驅動模塊管理不同的硬件設備。如果添加了新的硬件設備,那么需要編寫相應的硬件驅動模塊來管理它。對于一些常見的硬件設備, 系統已經自帶了相應的驅動,編譯內核時,選中它們,可以把它們編譯成內核的一部分,也可以以模塊的方式編譯。如果以模塊的方式編譯,那么可以在系統的 /lib/modules/`uname -r`目錄下找到對應的模塊文件。
比如,可以這樣找到相應的scsi驅動和usb驅動的模塊:
Quote: |
// 更新系統中文件索引數據庫(有點慢,不耐煩就按下CTRL+C取消掉) |
這些驅動的名字以.ko為后綴,在安裝系統時默認編譯為了模塊。實際上可以把它們編譯為內核的一部分,僅僅需要在編譯內核時選擇為
可通過查看/proc文件系統的modules文件檢查內核中已加載的各個模塊的狀態,也可以通過lsmod命令直接查看它們。
Quote: |
$ cat /proc/modules |
下面卸載usbhid模塊看看(呵呵,小心卸載scsi的驅動哦!因為你的系統就跑在上面,如果確實想玩玩,卸載前記得保存數據),通過rmmod命令就可以實現。
Quote: |
// 先切換到root用戶 |
如果你有個usb鼠標,那么移動一下,是不是發現動不了啦?因為設備驅動都沒有了,設備自然就沒法用羅。不過不要緊張,既然知道是什么原因,那么把設備驅動重新加載上就可以啦,下面用insmod把usbhid模塊重新加載上。
Quote: |
// 也要root用戶的 |
okay,現在鼠標又可以用啦,不信再動一下鼠標 :-)
到這里,硬件設備和設備驅動之間關系應該是比較清楚了吧。如果沒有,那么繼續下面的內容。
在Linux下,設備驅動關聯著相應的設備文件,而設備文件則和硬件設備一一對應(更多細節請參考資料[8][9][10])。這些設備文件都統一存放在系統的/dev/目錄下。
例如,scsi設備對應的/dev/sda,/dev/sda1,/dev/sda2...下面查看這些設備文件的信息。
Quote: |
$ ls -l /dev/sda* |
可以看到第一列第一個字符都是b,第五列都是數字8。b表示該文件是一個塊設備文件,對應地,如果是c則表示字符設備(例如/dev/ttyS0,關于塊設備和字符設備的區別,可以看這里[摘自網絡])。
Quote: |
字符設備:字符設備就是能夠像字節流一樣訪問的設備,字符終端和串口就屬于字符設備。 |
數字8則是該硬件設備在內核中對應的設備編號,可以在內核的Documentation/devices.txt文件中找到設備號分配情況。但是為什么同 一個設備會對應不同的設備文件(/dev/sda后面為什么還有不同的數字,而且ls結果中的第6列貌似和它們對應起來的)。這實際上是為了區分不同設備 的不同部分。對于硬盤,這樣可以處理硬盤內部的不同分區。就內核而言,它僅僅需要通過第5列的設備號就可以找到對應的硬件設備,但是對于驅動模塊來說,它 還需要知道如何處理不同的分區,于是就多了一個輔設備號,即第6列對應的內容。這樣一個設備就有了主設備號(第5列)和輔設備號(第6列),從而方便的實 現對各種硬件設備的管理。
因為設備文件和硬件是對應的,這樣我們可以直接從/dev/sda(如果是IDE的硬盤,那么對應的設備就是/dev/hda啦)設備中讀出硬盤的信息,例如:
Quote: |
// 用dd命令復制出硬盤的前512個字節,要root用戶哦 |
因為這些信息并不是很直觀(而且下面我們會進一步深入的分析),那么我們來看看另外一個設備文件,將可以非常直觀的演示設備文件和硬件的對應關系。還是以鼠標為例吧,下面來讀取鼠標對應的設備文件的信息。
Quote: |
// 同樣需要root用戶 |
移動鼠標看看,是不是發現有不同信息輸出。(基于這一原理,我們經常通過在一端讀取設備文件/dev/ttyS0中的內容,而在另一端往設備文件/dev/ttyS0中寫入內容來檢查串口線是否被損壞。)
到這里,對設備驅動、設備文件和硬件設備之間的關聯應該是印象更深刻了。如果想深入了解設備驅動的工作原理和設備驅動模塊的編寫,那么看看參考資料[10],開始你的設備驅動模塊的編寫歷程吧。
參考資料:
[5] Compile linux kernel 2.6
http://www.cyberciti.biz/tips/compiling-linux-kernel-26.html
[6] Linux系統的硬件驅動程序編寫原理
http://www.blue1000.com/bkhtml/2001-02/2409.htm
[7] Linux下USB設備的原理、配置、 常見問題
http://soft.zdnet.com.cn/software_zone/2007/1108/617545.shtml
[8] Linux 核心--9.設備驅動
http://www.bitscn.com/linux/driver/200604/6788.html
[9] The Linux Kernel Module Programming Guide
http://www.dirac.org/linux/writing/lkmpg/2.6/lkmpg-2.6.0.html
[10] Linux設備驅動開發
http://linuxdriver.co.il/ldd3/
1 理解、查看磁盤分區
實際上內存、u盤等都可以作為文件系統底層的“存儲”設備,但是這里我們僅用硬盤作為實例來介紹磁盤和分區的關系。
目前Linux的分區依然采用第一臺PC硬盤所使用的分區原理(見該部分的參考資料[1]),下面逐步分析和演示這一分區原理。
先來看看幾個概念:
A. 設備管理和分區
在Linux下,每一個存儲設備對應一個系統的設備文件,對于硬盤等IDE和SCSI設備,在系統的/dev目錄下可以找到對應的包含字符hd和sd的設 備文件。而根據硬盤連接的主板設備接口和數據線接口的不同,在hd或者sd字符后面可以添加一個從a到z的字符,例如hda,hdb,hdc和sda, sdb,sdc等,另外為了區別同一個硬件設備的不同分區,在后面還可以添加了一個數字,例如hda1,hda2,hda3...和sda1,sda2, sda3,所以你在/dev目錄下,可以看到很多類似的設備文件。
B. 各分區的作用
在分區的時候常遇到主分區和邏輯分區的問題,這實際上是為了方便擴展分區,正如后面的邏輯卷的引入是為了更好地管理多個硬盤一樣,引入主分區和邏輯分區可以方便地進行分區的管理。
在Linux系統中,每一個硬盤設備最多由4個主分區(包括擴展分區)構成。
主分區的作用是計算機用來進行啟動操作系統的,因此每一個操作系統的啟動程序或者稱作是引導程序,都應該存放在主分區上。Linux規定主分區(或者擴展 分區)占用分區編號中的前4個。所以你會看到主分區對應的設備文件為/dev/hda1-4或者/dev/sda1-4,而不會是hda5或者sda5。
擴展分區則是為了擴展更多的邏輯分區的,在Linux下,邏輯分區占用了hda5-16或者sda5-16等12個編號。
C. 分區類型
它規定了這個分區上的文件系統的類型。Linux支持諸如msdoc,vfat,ext2,ext3等諸多的文件系統類型,更多信息在下一小節進行進一步的介紹。
下面通過分析硬盤的前512個字節(即MBR)來分析和理解分區。
先來看看這張表(見附圖2),它用來描述MBR的結構。MBR包括引導部分、分區表、以及結束標記(55AAH),分別占用了512字節中446字節、 64字節和2字節。這里僅僅關注分區表部分,即中間的64字節以及圖中左邊的部分。(如果你對引導部分感興趣,請參考資料[10][11][12])
由于我用的是SCSI的硬盤,下面從/dev/sda設備中把硬盤的前512個字節拷貝到文件mbr.bin中。
Quote: |
// 先切換到root用戶 |
下面用file,od,fdisk等命令來分析這段MBR的數據,并對照附圖[2]以便加深理解。
Quote: |
$ file mbr.bin |
file命令的結果顯示,剛拷比的512字節是啟動扇區,用分號分開的幾個部分分別是boot loader,分區3和分區4。分區3的類型是82,即swap分區(可以通過fdisk命令的l命令列出相關信息),它對應fdisk的結果中 /dev/sda3所在行的第5列,分區3的扇區數是1959930,轉換成字節數是1959930*512(目前,硬盤的默認扇區大小是512字節), 而swap分區的默認塊大小是1024字節,這樣塊數就是:
Quote: |
$ echo 1959930*512/1024 | bc |
正好是fdisk結果中/dev/sda3所在行的第四列對應的塊數,同樣地,可以對照fdisk和file的結果分析分區4。
再來看看od命令以十六進制顯示的結果,同樣考慮分區3,計算一下發現,分區3對應的od命令的結果為:
Quote: |
fe00 ffff fe82 ffff 14c0 012a e7fa 001d |
首先是分區標記,00H,從圖[2]中,看出它就不是引導分區(80H標記的才是引導分區),而分區類型呢?為82H,和file顯示結果一致,現在再來關注一下分區大小,即file結果中的扇區數。
Quote: |
$ echo "ibase=10;obase=16;1959930" | bc |
剛好對應e7fa 001d,同樣地考慮引導分區的結果:
Quote: |
0180 0001 fe83 ffff 003f 0000 1481 012a |
分區標記:80H,正好反應了這個分區是引導分區,隨后是引導分區所在的磁盤扇區情況,010100,即1面0道1扇區。其他內容可以對照分析。
考慮到時間關系,更多細節請參考下面的資料或者查看看系統的相關手冊。
補充:安裝系統時,可以用fdisk,cfdisk等命令進行分區。如果要想從某個分區啟動,那么需要打上80H標記,例如可通過cfdisk把某個分區設置為bootable來實現。
參考資料:
[1] 解析磁盤、分區、文件系統
http://www.linuxpk.com/37190.html
[2] 硬盤分區表詳解
http://www.linuxpk.com/5378.html
[3] 深入理解Linux的硬盤分區
http://www.linuxpk.com/39733.html
[4] 什么是硬件分區表
http://www.pc-web.cn/pc/basic/465.asp
[5] Linux指導第6部分 使用分區和文件系統
http://www.pass100.net/jisuanji/linux/zhidao/80974.html
[10] 硬盤MBR全面分析
http://www.pc120.net.cn/home/datcb/05101223070444577.htm
[11] Inside the linux boot process
http://www-128.ibm.com/developerworks/linux/library/l-linuxboot/
[12] Develop your own OS: booting
http://docs.huihoo.com/gnu_linux/own_os/booting.htm
[13] Redhat 9磁盤分區簡介
http://www.topstudy.com/info/default.aspx?guid=eeac2894-3588-4b1a-9607-1ad377caa03f
[14] Linux partition HOWTO
http://mirror.lzu.edu.cn/tldp/HOWTO/Partition/
2 分區和文件系統的關系
在沒有引入邏輯卷之前,分區類型和文件系統類型幾乎可以同等對待,設置分區類型的過程就是格式化分區,建立相應的文件系統類型的過程。
下面主要介紹如何建立分區和文件系統類型的聯系,即如何格式化分區為指定的文件系統類型。
先來看看Linux下文件系統的常見類型(如果要查看所有Linux支持的文件類型,可以用fdisk命令的l命令查看,或者通過man fs查看,也可通過/proc/filesystems查看到當前內核支持的文件系統類型)
ext2,ext3:這兩個是Linux根文件系統通常采用的類型
swap:這個是具體實現Linux虛擬內存時采用的一種文件系統,安裝時一般需要建立一個專門的分區,并格式化為swap文件系統(如果想添加更多的 swap分區,那么可以參考本節的資料[1],熟悉dd,mkswap,swapon,swapoff等命令的用法)
proc:這是一種比較特別的文件系統,作為內核和用戶之間的一個接口存在,建立在內存中(你可以通過cat命令查看/proc系統下的文件,甚至可以通 過修改/proc/sys下的文件實時調整內核的配置,當前前提是你需要把proc文件系統掛載上[mount -t proc proc /proc])
除了這三個最常見的文件系統類型外,Linux支持包括vfat,iso,xfs,nfs在內各種常見的文件系統類型,在linux下,你可以自由地查看和操作windows等其他操作系統使用的文件系統。
那么如何建立磁盤和這些文件系統類型的關聯呢?格式化。
格式化的過程實際上就是重新組織分區的過程,可通過mkfs命令來實現,當然也可以通過fdisk等命令來實現。這里僅介紹mkfs,mkfs可用來對一 個已有的分區進行格式化,不能實現分區操作(如果要對一個磁盤進行分區和格式化,那么可以用fdisk就可以啦)。格式化后,相應的分區上的數據就通過某 種特別的文件系統類型進行組織了。
例如:把/dev/sda9分區格式化為ext3的文件系統。
Quote: |
// 先切換到root用戶 |
如果要列出各個分區的文件系統類型,那么可以用fdisk -l命令。
更多信息請參考下列資料。
參考資料:
[1] Linux下加載swap分區的步驟
http://soft.zdnet.com.cn/software_zone/2007/1010/545261.shtml
[2] 光碟的標準
http://www.edisc.com.cn/bike/viewnews.btml?id=274
[3] Linux下ISO鏡像文件的制作與刻錄
http://www.examda.com/linux/fudao/20071212/113445321.html
[4] RAM磁盤分區解釋
http://oldlinux.org/oldlinux/viewthread.php?tid=2677
http://www.ibm.com/developerworks/cn/linux/l-initrd.html
[5] 高級文件系統實現者指南
http://www-128.ibm.com/developerworks/search/searchResults.jsp?searchType=1&searchSite=dWChina&pageLang=zh&langEncoding=UTF8&searchScope=dW&query=%E9%AB%98%E7%BA%A7%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F%E5%AE%9E%E7%8E%B0%E8%80%85%E6%8C%87%E5%8D%97&Search.x=42&Search.y=9&Search=%E6%90%9C%E7%B4%A2
(有必要突出解釋swap,RAM文件系統的工作原理等)
3 分區、邏輯卷和文件系統的關系
在上一節中,我們直接把分區格式化為某種文件系統類型,但是考慮到擴展新的存儲設備的需要,開發人員在文件系統和分區之間引入了邏輯卷。考慮到時間關系,這里不再詳述,請參考資料[1]。
參考資料:
[1] Linux邏輯卷管理詳解
http://unix-cd.com/vc/www/28/2007-06/1178.html
[2] 見1.2的最后一個參考資料的最后一節
4 文件系統的可視化結構
文件系統最終呈現出來的是一種可視化的結構,我們可用ls,find,tree等命令把它呈現出來。它就像一顆倒掛的“樹”,在樹的節點上還可以掛載新的“樹”。(如果想把目錄結構以圖表的方式呈現出來,那么可以使用我之前寫的一個腳本,即參考資料[3])。
下面簡單介紹文件系統的掛載。
一個文件系統可以通過一個設備掛載(mount)到某個目錄下(具體的實現請參考資料[2]和[1]),這個目錄被稱為掛載點。有趣的是,在Linux 下,一個目錄本身還可以掛載到另外一個目錄下,一個格式化了的文件也可以通過一個特殊的設備/dev/loop進行掛載(如iso文件)。另外,就文件系 統而言,Linux不僅支持本地文件系統,還支持遠程文件系統(如nfs)。
下面簡單介紹文件系統掛載的幾個實例。
A. 根文件系統的掛載
Quote: |
// 掛載需要root權限,先切換到root用戶 |
B. 掛載一個新的設備
如果內核已經支持了USB接口,那么在插入u盤的時候,我們可以通過dmesg命令查看它對應的設備號,并掛載它。
Quote: |
// 查看dmesg結果中的最后幾行內容,找到類似/dev/sdN的信息,找出u盤對應的設備號 |
C. 掛載一個iso文件或者是光盤
對于一些iso文件或者是iso格式的光盤,同樣可以通過mount命令掛載。
Quote: |
// 對于iso文件 |
D. 掛載一個遠程文件系統
Quote: |
$ mount -t nfs remote_ip:/path/to/share_directory /path/to/local_directory |
proc文件系統組織在內存中,但是你可以把它掛載到某個目錄下。通常把它掛載在/proc目錄下,以便一些系統管理和配置工具使用它。例如top命令用 它分析內存的使用情況(讀取/proc/meminfo和/proc/stat等文件中的內容),lsmod命令通過它獲取內核模塊的狀態(讀取 /proc/modules),netstat命令通過它獲取網絡的狀態(讀取/proc/net/dev等文件),當然,你也可以編寫自己的相關工具。 除此之外,通過調整/proc/sys目錄下的文件,你可以動態的調整系統的配置,比如通過往 /proc/sys/net/ipv4/ip_forward文件中寫入數字1就可以讓內核支持數據包的轉發。(更多信息請參考proc的幫助,man proc)
F. 掛載一個目錄
Quote: |
$ mount --bind /path/to/needtomount_directory /path/to/mountpoint_directory |
這個非常有意思,比如你可以把某個目錄掛載到ftp服務的根目錄下,而無須把內容復制過去,就可以把相應目錄中的資源提供給別人共享。
以上都只提到了掛載,那怎么卸載呢?用umount命令跟上掛載的源地址或者掛載點(設備,文件,遠程目錄等)就可以。例如:
Quote: |
$ umount /path/to/mountpoint_directory |
如果想管理大量的或者經常性的掛載服務,那么每次手動掛載是很糟糕的事情。這個時候就可以利用mount的配置文件/etc/fstab,把mount對 應的參數寫到/etc/fstab文件對應的列中即可實現批量掛載(mount -a)和卸載(umount -a)。/etc/fstab中各列分別為文件系統、掛載點、類型、相關選項。更多信息可參考fstab的幫助(man fstab)。
參考資料:
[1] Linux硬盤分區以及其掛載原理
http://www.xxlinux.com/linux/article/accidence/technique/20070521/8493.html
[2] 從文件I/O看linux的虛擬文件系統
http://www.ibm.com/developerworks/cn/linux/l-cn-vfs/
[3] 用Graphviz進行可視化操作──繪制函數調用關系圖
http://oss.lzu.edu.cn/blog/blog.php?/do_showone/tid_1425.html
5 如何制作一個文件系統
Linux的文件系統下有一些最基本的目錄,不同的目錄下存放著不同作用的各類文件。最基本的目錄有/etc, /lib, /dev, /bin等,它們分別存放著系統配置文件,庫文件,設備文件和可執行程序。這些目錄一般情況下是必須的,在做嵌入式開發的時候,我們需要手動或者是用 busybox等工具來創建這樣一個基本的文件系統。如何來制作一個這樣的文件系統呢?請參考資料[1]和[2]。這里我們制作僅制作一個非常簡單的文件 系統,并對該文件系統進行各種常規的操作,以便加深對文件系統的理解。
首先,創建一個固定大小的文件。
Quote: |
// 還記得dd命令么?我們就用它來產生一個固定大小的文件,這個為1M(1024*1024 bytes)的文件 |
說明:/dev/zero是一個非常特殊的設備,如果讀取它,可以獲取任意多個\0。
接著把該文件格式化為某個指定文件類型的文件系統。(是不是覺得不可思議,文件也可以格式化?是的,不光是設備可以,文件也可以以某種文件系統類型進行組織,但是需要注意的是,某些文件系統(如ext3)要求被格式化的目標最少有64M的空間)。
Quote: |
// 格式化文件 |
因為該文件以文件系統的類型組織了,那么可以用mount命令掛載并使用它。
Quote: |
// 請切換到root用戶掛載它,并通過-o loop選項把它關聯到一個特殊設備/dev/loop |
在該文件系統下進行各種常規操作,包括讀、寫、刪除等。(每次操作前先把minifs文件保存一份,以便比較,結合相關資料就可以深入地分析各種操作對文件系統的改變情況,從而深入理解文件系統作為一種組織數據的方式的實現原理等)
Quote: |
$ cp minifs minifs.bak |
上面僅僅演示了一些分析文件系統的常用工具,并分析了幾個常規的操作,如果你想非常深入地理解文件系統的實現原理,請熟悉使用上述工具并閱讀相關資料,比如1節的參考資料[2][3][4]。
參考資料:
[1] Build a mini filesystem in linux from scratch
http://oss.lzu.edu.cn/blog/blog.php?/do_showone/tid_1211.html
[2] Build a mini filesystem in linux with BusyBox
http://oss.lzu.edu.cn/blog/blog.php?/do_showone/tid_1212.html
[3] ext2 文件系統
http://man.chinaunix.net/tech/lyceum/linuxK/fs/filesystem.html
6 如何開發自己的文件系統
隨著fuse的出現,在用戶空間開發文件系統成為可能,如果想開發自己的文件系統,那么閱讀下面的參考資料吧。
參考資料:
[1] 使用fuse開發自己的文件系統
后記:
[1] 2007年12月22日,收集了很多資料,寫了整體的框架。
[2] 2007年12月28日下午,完成初稿,考慮到時間關系,很多細節也沒有進一步分析,另外有些部分可能存在理解上的問題,歡迎批評指正。
[3] 2007年12月28日晚,修改部分資料,并正式公開該篇文檔。
[4] 29號,添加設備驅動和硬件設備一小節。