在 Unix 的世界里,有句很經典的話:一切對象皆是文件。這句話的意思是說,可以將 Unix 操作系統(tǒng)中所有的對象都當成文件,然后使用操作文件的接口來操作它們。Linux 作為一個類 Unix 操作系統(tǒng),也努力實現這個目標。
虛擬文件系統(tǒng)簡介
為了實現一切對象皆是文件這個目標,Linux 內核提供了一個中間層:虛擬文件系統(tǒng)(Virtual File System)。
如果大家使用過面向對象編程語言(如C++/Java等)的話,應該對接口這個概念并不陌生。而虛擬文件系統(tǒng)類似于面向對象中的接口,定義了一套標準的接口。開發(fā)者只需要實現這套接口,即可以使用操作文件的接口來操作對象。如下圖所示:

上圖中的藍色部分就是虛擬文件系統(tǒng)所在位置。
從上圖可以看出,虛擬文件系統(tǒng)為上層應用提供了統(tǒng)一的接口。如果某個文件系統(tǒng)實現了虛擬文件系統(tǒng)的接口,那么上層應用就能夠使用諸如open()、read()和write()等函數來操作它們。
今天,我們就來介紹虛擬文件系統(tǒng)的原理與實現。
虛擬文件系統(tǒng)原理
在闡述虛擬文件系統(tǒng)的原理前,我們先來介紹一個 Java 例子。通過這個 Java 例子,我們能夠更容易理解虛擬文件系統(tǒng)的原理。
一個Java例子
如果大家使用過 Java 編寫程序的話,那么就很容易理解虛擬文件系統(tǒng)了。我們使用 Java 的接口來模擬虛擬文件系統(tǒng)的定義:
publicinterfaceVFSFile{
intopen(Stringfile,intmode);
intread(intfd,byte[]buffer,intsize);
intwrite(intfd,byte[]buffer,intsize);
...
}
上面定義了一個名為VFSFile的接口,接口中定義了一些方法,如open()、read()和write()等?,F在我們來定義一個名為Ext3File的對象來實現這個接口:
publicclassExt3FileimplementsVFSFile{
@Override
publicintopen(Stringfile,intmode){
...
}
@Override
publicintread(intfd,byte[]buffer,intsize){
...
}
@Override
publicintwrite(intfd,byte[]buffer,intsize){
...
}
...
}
現在我們就能使用VFSFile接口來操作Ext3File對象了,如下代碼:
publicclassMain(){
publicstaticvoidmain(String[]args){
VFSFilefile=newExt3File();
intfd=file.open("/tmp/file.txt",0);
...
}
}
從上面的例子可以看出,底層對象只需要實現VFSFile接口,就可以使用VFSFile接口相關的方法來操作對象,用戶完全不需要了解底層對象的實現過程。
虛擬文件系統(tǒng)原理
上面的 Java 例子已經大概說明虛擬文件系統(tǒng)的原理,但由于 Linux 是使用 C 語言來編寫的,而 C 語言并沒有接口這個概念。所以,Linux 內核使用了一些技巧來模擬接口這個概念。
下面來介紹一下 Linux 內核是如何實現的。
1. file結構
為了模擬接口,Linux 內核定義了一個名為file的結構體,其定義如下:
structfile{ ... conststructfile_operations*f_op; ... };
在 file 結構中,最為重要的一個字段就是f_op,其類型為file_operations結構。而file_operations結構是由一組函數指針組成,其定義如下:
structfile_operations{
...
loff_t(*llseek)(structfile*,loff_t,int);
ssize_t(*read)(structfile*,char__user*,size_t,loff_t*);
ssize_t(*write)(structfile*,constchar__user*,size_t,loff_t*);
...
int(*open)(structinode*,structfile*);
...
};
從file_operations結構的定義可以隱約看到接口的影子,所以可以猜想出,如果實現了file_operations結構中的方法,應該就能接入到虛擬文件系統(tǒng)中。
在 Linux 內核中,file結構代表著一個被打開的文件。所以,只需要將file結構的f_op字段設置成不同文件系統(tǒng)實現好的方法集,那么就能夠使用不同文件系統(tǒng)的功能。
這個過程在__dentry_open()函數中實現,如下所示:
staticstructfile*
__dentry_open(structdentry*dentry,
structvfsmount*mnt,
tructfile*f,
int(*open)(structinode*,structfile*),
conststructcred*cred)
{
...
inode=dentry->d_inode;
...
//設置file結構的f_op字段為底層文件系統(tǒng)實現的方法集
f->f_op=fops_get(inode->i_fop);
...
returnf;
}
設置好file結構的f_op字段后,虛擬文件系統(tǒng)就能夠使用通用的接口來操作此文件了。調用過程如下:

2. file_operations結構
底層文件系統(tǒng)需要實現虛擬文件系統(tǒng)的接口,才能被虛擬文件系統(tǒng)使用。也就是說,底層文件系統(tǒng)需要實現file_operations結構中的方法集。
一般底層文件系統(tǒng)會在其內部定義好file_operations結構,并且填充好其方法集中的函數指針。如minix文件系統(tǒng)就定義了一個名為minix_file_operations的file_operations結構。其定義如下:
//文件:fs/minix/file.c
conststructfile_operationsminix_file_operations={
.llseek=generic_file_llseek,
.read=do_sync_read,
.aio_read=generic_file_aio_read,
.write=do_sync_write,
.aio_write=generic_file_aio_write,
.mmap=generic_file_mmap,
.fsync=generic_file_fsync,
.splice_read=generic_file_splice_read,
};
也就是說,如果當前使用的是 minix 文件系統(tǒng),當使用read()函數讀取其文件的內容時,那么最終將會調用do_sync_read()函數來讀取文件的內容。
3. dentry結構
到這里,虛擬文件系統(tǒng)的原理基本分析完畢,但還有兩個非常重要的結構要介紹一下的:dentry和inode。
dentry結構表示一個打開的目錄項,當我們打開文件/usr/local/lib/libc.so文件時,內核會為文件路徑中的每個目錄創(chuàng)建一個dentry結構。如下圖所示:

可以看到,file結構有個指向dentry結構的指針,如下所示:
structfile{
...
structpathf_path;
...
conststructfile_operations*f_op;
...
};
structpath{
...
structdentry*dentry;
};
與文件類似,目錄也有相關的操作接口,所以在dentry結構中也有操作方法集,如下所示:
structdentry{
...
structdentry*d_parent;//父目錄指針
structqstrd_name;//目錄名字
structinode*d_inode;//指向inode結構
...
conststructdentry_operations*d_op;//操作方法集
...
};
其中的d_op字段就是目錄的操作方法集。
內核在打開文件時,會為路徑中的每個目錄創(chuàng)建一個dentry結構,并且使用d_parent字段來指向其父目錄項,這樣就能通過d_parent字段來追索到根目錄。
4. inode結構
在 Linux 內核中,inode結構表示一個真實的文件。為什么有了dentry結構還需要inode結構呢?這是因為 Linux 存在硬鏈接的概念。
例如使用以下命令為/usr/local/lib/libc.so文件創(chuàng)建一個硬鏈接:
ln/usr/local/lib/libc.so/tmp/libc.so
現在/usr/local/lib/libc.so和/tmp/libc.so指向同一個文件,但它們的路徑是不一樣的。所以,就需要引入inode結構了。如下圖所示:

由于/usr/local/lib/libc.so和/tmp/libc.so指向同一個文件,所以它們都使用同一個inode對象。
inode 結構保存了文件的所有屬性值,如文件的創(chuàng)建時間、文件所屬用戶和文件的大小等。其定義如下所示:
structinode{
...
uid_ti_uid;//文件所屬用戶
gid_ti_gid;//文件所屬組
...
structtimespeci_atime;//最后訪問時間
structtimespeci_mtime;//最后修改時間
structtimespeci_ctime;//文件創(chuàng)建時間
...
unsignedshorti_bytes;//文件大小
...
conststructfile_operations*i_fop;//文件操作方法集(用于設置file結構)
...
};
我們注意到 inode 結構有個類型為file_operations結構的字段i_fop,這個字段保存了文件的操作方法集。當用戶調用open()系統(tǒng)調用打開文件時,內核將會使用inode結構的i_fop字段賦值給file結構的f_op字段。我們再來重溫下賦值過程:
staticstructfile*
__dentry_open(structdentry*dentry,
structvfsmount*mnt,
tructfile*f,
int(*open)(structinode*,structfile*),
conststructcred*cred)
{
...
//文件對應的inode對象
inode=dentry->d_inode;
...
//使用inode結構的i_fop字段賦值給file結構的f_op字段
f->f_op=fops_get(inode->i_fop);
...
returnf;
}
總結
本文主要介紹了虛擬文件系統(tǒng)的基本原理,從分析中可以發(fā)現,虛擬文件系統(tǒng)使用了類似于面向對象編程語言中的接口概念。正是有了虛擬文件系統(tǒng),Linux 才能支持各種各樣的文件系統(tǒng)。
審核編輯:劉清
-
JAVA
+關注
關注
20文章
3001瀏覽量
116449 -
UNIX操作系統(tǒng)
+關注
關注
0文章
13瀏覽量
15627 -
C語言
+關注
關注
183文章
7644瀏覽量
145614 -
LINUX內核
+關注
關注
1文章
321瀏覽量
23214
原文標題:細說 Linux 虛擬文件系統(tǒng)原理
文章出處:【微信號:LinuxHub,微信公眾號:Linux愛好者】歡迎添加關注!文章轉載請注明出處。
發(fā)布評論請先 登錄
Linux平臺/proc虛擬文件系統(tǒng)詳解
Linux虛擬文件系統(tǒng)的基礎知識
STM32F429 sdio虛擬文件系統(tǒng)的相關資料推薦
VFS虛擬文件系統(tǒng)描述
Linux虛擬文件系統(tǒng)實現技術探討
Xilinx Zynq制作修改根文件系統(tǒng)的方法
linux 虛擬文件可以系統(tǒng)實現
嵌入式Linux常用文件系統(tǒng)
簡單介紹Linux虛擬文件系統(tǒng)–VFS
嵌入式Linux文件系統(tǒng)詳細介紹
linux文件系統(tǒng)中的虛擬文件系統(tǒng)設計詳解
如何區(qū)別Linux文件系統(tǒng)呢?
深入剖析Linux內核虛擬文件系統(tǒng)
介紹虛擬文件系統(tǒng)的原理與實現
評論