$ updatedb
尋找scsi 相關的驅動
$ locate scsi*.ko
尋找usb 相關的驅動
$ locate usb*.ko
這些驅動以
.ko為後綴,在安裝系統時預設編譯為了模組。實際上可以把它們編譯為核心的一部分,只需要在編譯核心時選擇為
[*]即可。但是,很多情況下會以模組的方式編譯它們,這可以減少核心的大小,並根據需要靈活地載入和卸載它們。以下簡單地示範如何卸載模組、載入模組以及查看已載入模組的狀態。
可透過
/proc檔案系統的
modules文件檢查內核中已載入的各個模組的狀態,也可以透過
lsmod命令直接查看它們。
$ cat /proc/modules
或者
$ lsmod
查看scsi 和usb 相關驅動,結果各列為模組名、模組大小、被其他模組的引用情況(引用次數、引用它們的模組)
$ lsmod | egrep scsi|usbusbhid 29536 0hid 28928 1 usbhidusbcore 138632 4 usbhid,ehci_hcd,ohci_hcdscsi_mod 147084 4 ,sr,sr_mod, ,libcd
下面卸載
usbhid模組看看(不要卸載scsi的驅動器!因為你的系統可能就跑在上面,如果確實想玩玩,卸載前記得保存數據),通過
rmmod指令就可以實現,先切換到Root 用戶:
$ sudo -s# rmmod usbhid
再查看該模組的信息,已經看不到了吧
$ lsmod | grep ^usbhid
如果有個usb 滑鼠,那麼移動一下,是不是發現動不了啦?因為設備驅動都沒有了,設備自然就無法用羅。不過不要緊張,既然知道原因,那麼重新加載驅動就可以,下面用
insmod把
usbhid模組重新加載上。
$ sudo -s# insmod `locate usbhid.ko`
locate usbhid.ko是為了找出
usbhid.ko模組的路徑,如果之前沒有
updatedb,估計用它是找不到了,不過也可以直接到
/lib/modules目錄下用
find把
usbhid.ko文件找到。
# insmod $(find /lib/modules -name *usbhid.ko* | grep `uname -r`)
現在滑鼠又可以用啦,不信再動一下滑鼠:-)
到這裡,硬體設備和設備驅動之間關係應該是比較清楚了。如果沒有,那麼繼續下面的內容。
Linux 裝置驅動關聯著對應的裝置文件,而裝置檔案則和硬體裝置一一對應。這些設備文件都統一存放在系統的
/dev/目錄下。
例如,scsi 設備對應
/dev/sda,
/dev/sda1,
/dev/sda2... 下面查看這些設備資訊。
$ ls -l /dev/sda*brw-rw---- 1 root disk 8, 0 2007-12-28 22:49 /dev/sdabrw-rw---- 1 root disk 8, 1 2007-12- 28 22:50 /dev/sda1brw-rw---- 1 root disk 8, 3 2007-12-28 22:49 /dev/sda3brw-rw---- 1 root disk 8, 4 2007-12-28 22:49 /dev/sda4brw-rw---- 1 root disk 8, 5 2007- 12-28 22:50 /dev/sda5brw-rw---- 1 root disk 8, 6 2007-12-28 22:50 /dev/sda6brw-rw---- 1 root disk 8, 7 2007-12-28 22:50 /dev/sda7brw-rw---- 1 root disk 8, 8 2007-12-28 22:50 /dev/sda8
可以看到第一列第一個字元都是
b,第五列都是數字8 。
b表示該文件是一個區塊設備文件,對應地,如果是
c則表示字元裝置(例如`/dev/ttyS0),關於區塊裝置和字元裝置的區別,可以看這裡:
字元設備:字元設備就是能夠像位元組流一樣存取的設備,字元終端機和字串口就屬於字元設備。
塊設備:塊設備上可以容納檔案系統。與字元裝置不同,在讀寫時,區塊裝置每次只能傳送一個或多個完整的區塊。在Linux 作業系統中,應用程式可以像存取字元裝置一樣讀寫區塊裝置(一次讀取或寫入任意的位元組資料)。因此,區塊設備和字元設備的區別只是在核心中對於資料的管理不同。
數字8 則是該硬體設備在核心中對應的設備編號,可以在內核的
Documentation/devices.txt和
/proc/devices文件中找到設備號分配狀況。但是為什麼同一個設備會對應不同的設備檔案(
/dev/sda後面為什麼還有不同的數字,而且
ls結果中的第6 列和它們對應起來)。這實際上是為了區分不同設備的不同部分。對於硬碟,這樣可以處理硬碟內部的不同分割區。就核心而言,它僅需要通過第5 列的設備號就可以找到對應的硬體設備,但是對於驅動模組來說,它還需要知道如何處理不同的分區,於是就多了一個輔設備號,即第6 列對應的內容。這樣一個設備就有了主設備號(第5 列)和輔設備號(第6 列),以便方便地實現對各種硬體設備的管理。
因為設備文件和硬體是對應的,這樣可以直接從
/dev/sda(如果是
IDE的硬碟,那麼對應的設備就是
/dev/hda啦)設備中讀出硬碟的訊息,例如:
用
dd指令複製出硬碟的前512 個字節,要Root 用戶
$ sudo dd if=/dev/sda of=mbr.bin bs=512 count=1
用
file命令查看相應的信息
$ file mbr.binmbr.bin: x86 boot sector, LInux i386 boot LOader; partition 3: ID=0x82, starthead 254, startsector 19535040, 1959930 sectors; partition 4:025040, 1959930 sectors; 56661255 sectors, code offset 0x48
也可以用
od命令以16 進制的形式讀取並進行分析
$ od -x mbr.bin
bs是區塊的大小(以位元組
bytes為單位),
count是塊數
因為這些資訊並不直觀(而且下面會進一步深入分析),那麼先來看看另外一個設備文件,將可以非常直觀地演示設備文件和硬體的對應關係。還是以滑鼠為例吧,下面來讀取滑鼠對應的裝置檔案的資訊。
$ sudo -s# cat /dev/input/mouse1 | od -x
你的滑鼠驅動可能不太一樣,所以裝置檔案可能是其他的,但是都會在
/dev/input下。
移動滑鼠看看,是不是發現有不同資訊輸出。基於這一原理,我們經常透過在一端讀取設備文件
/dev/ttyS0中的內容,而在另一端往設備文件
/dev/ttyS0中寫入內容來檢查串口線是否損壞。
到這裡,對設備驅動、設備文件和硬體設備之間的關聯應該是印象更深刻了。如果想深入了解裝置驅動的工作原理和裝置驅動的編寫,那麼看看下面列出的相關資料,開始裝置驅動的編寫歷程吧。
參考資料:
Compile linux kernel 2.6
Linux 系統的硬體驅動程式編寫原理
Linux 下USB設備的原理、設定、常見問題
The Linux Kernel Module Programming Guide
Linux 裝置驅動開發
實際上記憶體、u 盤等都可以作為檔案系統底層的「儲存」設備,但這裡僅用硬碟作為實例來介紹磁碟和分割區的關係。
目前Linux 的分區依然採用第一台PC硬碟所使用的分區原理,以下逐步分析並示範這一分區原理。
先來看看幾個概念:
設備管理和分區
Linux 下,每一個儲存裝置對應一個系統的裝置文件,對於硬碟等
IDE和
SCSI設備,在系統的
/dev目錄下可以找到對應的包含字符
hd和
sd的設備文件。而根據硬碟連接的主機板設備介面和數據線介面的不同,在
hd或者
sd字元後面可以添加一個從
a到
z的字符,例如
hda,
hdb,
hdc和
sda,
sdb,
sdc等,另外為了區別同一個硬體設備的不同分區,在後面還可以添加了一個數字,例如
hda1,
hda2,
hda3和
sda1,
sda2,
sda3,所以在
/dev目錄下,可以看到很多類似的設備檔案。
各分區的作用
在分割區時常遇到主分割區和邏輯分割區的問題,這其實是為了方便擴展分割區,正如後面的邏輯磁碟區的引入是為了更好地管理多個硬碟一樣,引入主分割區和邏輯分割區可以方便地進行分區的管理。
Linux 系統中每一個硬碟裝置最多由4 個主分割區(包含擴充分割區)所構成。
主分區的作用是電腦用來進行啟動作業系統的,因此每一個作業系統的啟動程序或稱作是引導程序,都應該存放在主分區上。 Linux 規定主分割區(或擴充分割區)佔用分割區編號中的前4 個。所以會看到主分割區對應的裝置檔為
/dev/hda1-4或者
/dev/sda1-4,而不會是
hda5或者
sda5。
擴展分割區則是為了擴展更多的邏輯分割區的,在Linux 下,邏輯分割區佔用了
hda5-16或者
sda5-16等12 個編號。
分區類型
它規定了這個分區上的檔案系統的類型。 Linux支援諸如msdoc,vfat,ext2,ext3等諸多的檔案系統類型,更多資訊在下一小節進行進一步的介紹。
下面透過分析硬碟的前512 個位元組(即
MBR)來分析和理解分區。
先來看看這張圖:
它用來描述
MBR的結構。
MBR包括引導部分、分割表、以及結束標記`(55AAH),分別佔用了512 個位元組中446 個位元組、 64 個位元組和2 個位元組。這裡僅僅關注分區表部分,即中間的64 位元組以及圖中左邊的部分。
由於我用的是
SCSI的硬碟,下面從
/dev/sda設備中把硬碟的前512 個位元組拷貝到文件
mbr.bin中。
$ sudo -s# dd if=/dev/sda of=mbr.bin bs=512 count=1
下面用
file,
od,
fdisk等指令來分析這段
MBR的數據,並對照上圖以便加深理解。
$ file mbr.binmbr.bin: x86 boot sector, LInux i386 boot LOader; partition 3: ID=0x82, starthead 254, startsector 19535040, 1959930 sectors; partition 4:025040, 1959930 sectors; 56661255 sectors, code offset 0x48$ od -x mbr.bin | tail -6 #只關注中間的64字節,所以截取了結果中後6行0000660 0000 0000 0000 0000 a666 ffff 003f 0000 1481 012a 00000000720 0000 0000 0000 0000 0000 0000 0000 fe000000740 ffff fe82001 12a efad fe000000760 ffff fe05 ffff fcba 0147 9507 0360 aa55$ sudo -s# fdisk -l | grep ^/ #僅分析MBR相關的部分,不分析邏輯分區部分/dev/sda1 * 1 1216 9767483132/dev/sda1 * 1 1216 976748313137/devs 1338 979965 82 Linux swap / Solaris/dev/sda4 1339 4865 28330627+ 5 Extended
file指令的結果顯示,剛拷貝的512 位元組是啟動磁區,用分號分開的幾個部分分別是
bootloader,分區3 和分區4 。分區3 的類型是82,即
swap分區(可以透過
fdisk命令的
l命令列出相關資訊),它對應
fdisk的結果中
/dev/sda3所在行的第5 列,分割區3 的磁區數是1959930,轉換成位元組數是
1959930*512(目前,硬碟的預設磁區大小是512 位元組),而
swap分區的預設區塊大小是1024 位元組,這樣區塊數就是
:
$ echo 1959930*512/1024 | bc979965
正好是
fdisk結果中
/dev/sda3所在行的第四列對應的區塊數,同樣地,可以對照
fdisk和
file的結果分析分區4 。
再來看看
od指令以十六進位顯示的結果,同樣考慮分區3,計算一下發現,分區3 對應的
od命令的結果為:
fe00 ffff fe82 ffff 14c0 012a e7fa 001d
首先是分區標記,
00H,從上圖中,看出它就不是引導分區(
80H標記的才是引導分割區),而分割區類型呢?為
82H,和
file顯示結果一致,現在再來關註一下分割區大小,即
file結果中的扇區數。
$ echo ibase=10;obase=16;1959930 | bc1DE7FA
剛好對應
e7fa 001d,同樣地考慮引導分割區的結果:
0180 0001 fe83 ffff 003f 0000 1481 012a
分區標記:
80H,剛好反應了這個分割區是開機分割區,接著是開機分割區所在的磁碟區情況,010100,即1 面0 道1 區。其他內容可以對照分析。
考慮到時間關係,更多細節請參考下面的資料或查看看系統的相關手冊。
補充:安裝系統時,可用
fdisk,
cfdisk等命令進行分區。如果要從某個分區啟動,那麼就需要打上
80H標記,例如可透過
cfdisk把某個分區設定為
bootable來實現。
參考資料:
Inside the linux boot process
Develop your own OS: booting
Redhat9 磁碟分割區簡介
Linux partition HOWTO
在沒有引入邏輯磁碟區之前,分割區類型和檔案系統類型幾乎可以同等對待,設定分割區類型的過程就是格式化分割區,建立對應的檔案系統類型的過程。
以下主要介紹如何建立分割區和檔案系統類型的聯繫,即如何格式化分割區為指定的檔案系統類型。
先來看看Linux 下檔案系統的常見類型(如果要查看所有Linux 支援的檔案類型,可以用
fdisk命令的
l命令查看,或透過
man fs查看,也可透過
/proc/filesystems查看到目前核心支援的檔案系統類型)
ext2,
ext3,
ext4:這三個是Linux 根檔案系統通常採用的類型
swap:這是實作Linux 虛擬記憶體時採用的一種檔案系統,安裝時一般需要建立一個專門的分割區,並且格式化為
swap檔案系統(如果想增加更多
swap分區,可以參考本節的參考資料,熟悉
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的檔案系統。
$ sudo -s# mkfs -t ext3 /dev/sda9
如果要列出各個分區的檔案系統類型,那麼可以用
fdisk -l命令。
更多資訊請參考下列資料。
參考資料:
Linux 下載入swap 分割區的步驟
Linux 下ISO 鏡像檔的製作與燒錄
RAM 磁碟分割區解釋:[1],[2]
高階檔案系統實現者指南
上一節直接把分割區格式化為某種檔案系統類型,但是考慮到擴展新的儲存裝置的需要,開發人員在檔案系統和分割區之間引入了邏輯磁碟區。考慮到時間關係,這裡不再詳述,請參考資料:Linux 邏輯磁碟區管理詳解
檔案系統最終呈現出來的是一種可視化的結構,可用ls,find,tree等指令把它呈現出來。它就像一顆倒掛的“樹”,在樹的節點上還可以掛載新的“樹”。
下面簡單介紹檔案系統的掛載。
一個檔案系統可以透過一個設備掛載(
mount)到某個目錄下,這個目錄稱為掛載點。有趣的是,在Linux 下,一個目錄本身還可以掛載到另一個目錄下,一個格式化了的檔案也可以透過一個特殊的設備
/dev/loop進行掛載(如
iso文件)。另外,就檔案系統而言,Linux 不僅支援本機檔案系統,還支援遠端檔案系統(如
nfs)。
下面簡單介紹幾個檔案系統掛載的實例。
根檔案系統的掛載
掛載需要Root 權限,例如,掛載系統根檔案系統
/dev/sda1到
/mnt
$ sudo -s# mount -t ext3 /dev/sda1 /mnt/
查看
/dev/sda1的掛載情況,可以看到,一個設備可以多次掛載
$ mount | grep sda1/dev/sda1 on / type ext3 (rw,errors=remount-ro)/dev/sda1 on /mnt type ext3 (rw)
對於一個已經掛載的檔案系統,為支援不同屬性可以重新掛載
$ mount -n -o remount, rw /
掛載一個新增設備
如果核心已經支援USB 接口,那麼插入u 碟時,可以透過
dmesg命令查看對應的設備號,並掛載它。
查看
dmesg結果中的最後幾行內容,找到類似
/dev/sdN的訊息,找出u 盤對應的設備號
$ dmesg
這裡假設u 磁碟是
vfat格式,以便在一些列印商店的Windows 上也可使用
# mount -t vfat /dev/sdN /path/to/mountpoint_directory
掛載一個iso 檔案或是光碟
對於一些iso檔案或是iso 格式的光碟,同樣可以透過
mount命令掛載。
對於iso 檔:
# mount -t iso9660 /path/to/isofile /path/to/mountpoint_directory
對於光碟:
# mount -t iso9660 /dev/cdrom /path/to/mountpoint_directory
掛載一個遠端檔案系統
# mount -t nfs remote_ip:/path/to/share_directory /path/to/local_directory
掛載一個proc 檔案系統
# mount -t proc proc /proc
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)
掛載一個目錄
$ mount --bind /path/to/needtomount_directory /path/to/mountpoint_directory
這很有趣,例如可以把某個目錄掛載到ftp 服務的根目錄下,而無須把內容複製過去,就可以把相應目錄中的資源提供給別人共享。
以上都只提到了掛載,那要怎麼卸載?用
umount指令跟上掛載的來源位址或掛載點(設備,文件,遠端目錄等)就可以。例如:
$ umount /path/to/mountpoint_directory
或者
$ umount /path/to/mount_source
如果想管理大量的或經常性的掛載服務,那麼每次手動掛載是很糟糕的事情。這時就可利用
mount的設定檔
/etc/fstab,把
mount對應的參數寫到
/etc/fstab文件對應的列中即可實現批量掛載(
mount -a)和卸載(
umount -a)。
/etc/fstab中各列分別為檔案系統、掛載點、類型、相關選項。更多資訊可參考
fstab的幫助(
man fstab)。
參考資料:
Linux 硬碟分割區以及其掛載原理
從檔案I/O 看Linux 的虛擬檔案系統
原始碼分析:靜態分析C 程式函數呼叫關係圖
Linux 檔案系統下有一些最基本的目錄,不同的目錄下存放著不同作用的各類檔案。最基本的目錄有
/etc,
/lib,
/dev,
/bin等,它們分別存放著系統配置文件,庫文件,設備文件和可執行程序。這些目錄一般情況下是必須的,在做嵌入式開發時,需要手動或是用
busybox等工具來創建這樣一個基本的檔案系統。這裡僅製作一個非常簡單的檔案系統,並對該檔案系統進行各種常規操作,以便加深對檔案系統的理解。
還記得
dd命令麼?就用它來產生一個固定大小的文件,這個為
1M(1024*1024 bytes)的文件
$ dd if=/dev/zero of=minifs bs=1024 count=1024
查看文件類型,這裡的
minifs是一個充滿
\0的文件,沒有任何特定的資料結構
$ file minifsminifs: data
說明:
/dev/zero是一個非常特殊的設備,如果讀取它,可以取得任意多個
\0。
接著把該文件格式化為某個指定文件類型的文件系統。 (是不是覺得不可思議,文件也可以格式化?是的,不光是設備可以,文件也可以以某種文件系統類型進行組織,但是需要注意的是,某些文件系統(如
ext3)要求被格式化的目標最少有
64M的空間)。
$ mkfs.ext2 minifs
查看此時的文件類型,這時文件
minifs就以
ext2文件系統的格式組織了
$ file minifsminifs: Linux rev 1.0 ext2 filesystem data
因為該文件以文件系統的類型組織了,那麼可以用
mount命令掛載並使用它。
請切換到
root用戶掛載它,並透過
-o loop選項把它關聯到一個特殊設備
/dev/loop
$ sudo -s# mount minifs /mnt/ -o loop
查看該檔案系統訊息,僅可以看到一個目錄文件
lost+found
$ ls /mnt/lost+found
在該檔案系統下進行各種常規操作,包括讀取、寫入、刪除等。 (每次操作前先把
minifs文件保存一份,以便比較,結合相關資料就可以深入地分析各種操作對文件系統的改變情況,從而深入理解文件系統作為一種組織數據的方式的實現原理等)
$ 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
建立一個檔案後,比較此時檔案系統和先前檔案系統的異同
$ diff orig.od touch.oddiff orig.od touch.od61,63c61,64< 0060020 000c 0202 2e2e 0000 000b 0000 03e8 020a< 00604000b 0000 03e8 020a< 006040076467 0000 0000 0000< 0060060 0000 0000 0000 0000 0000 0000 0000 0000---> 0060020 000c 0202 000e 0b> 0060040 6f6c 7473 662b 756f 646e 0000 000c 0000> 0060060 03d4 0105 6568 6c6c 006f 0000 000000 0000 0000 0000 0000 0000 0000
透過比較發現:新增文件,檔案系統的相應位置發生了明顯的變化
$ echo hello, world > /mnt/hello
執行
sync指令,確保快取中的資料已經寫入磁碟(還記得本節圖1 的
buffer cache吧,這裡就是把
cache中的資料寫到磁碟中)
$ sync$ cp minifs minifs-echo.bak$ od -x minifs-echo.bak > echo.od
寫入檔案內容後,比較檔案系統和先前的異同
$ diff touch.od echo.od
查看檔案系統中的字串
$ strings minifslost+foundhellohello, world
刪除
hello文件,查看檔案系統變化
$ rm /mnt/hello$ cp minifs minifs-rm.bak$ od -x minifs-rm.bak > rm.od$ diff echo.od rm.od
透過查看檔案系統的字串發現:刪除檔案時並沒有覆蓋檔案內容,所以從理論上說內容此時還是可恢復的
$ strings minifslost+foundhellohello, world
上面僅僅示範了一些分析文件系統的常用工具,並分析了幾個常規的操作,如果想非常深入地理解文件系統的實現原理,請熟悉使用上述工具並閱讀相關資料。
參考資料:
Build a mini filesystem in linux from scratch
Build a mini filesystem in linux with BusyBox
ext2 檔案系統
隨著
fuse的出現,在使用者空間開發檔案系統成為可能,如果想開發自己的檔案系統,那麼推薦閱讀:使用fuse 開發自己的檔案系統。
2007 年12 月22 日,收集了許多資料,寫了整體的框架
2007 年12 月28 日下午,完成初稿,考慮到時間關係,許多細節也沒有進一步分析,另外有些部分可能存在理解上的問題,歡迎批評指正
2007 年12 月28 日晚,修改部分資料,並正式公開該文件
29 號,增加設備驅動和硬體設備一小節