<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          細(xì)說|Linux虛擬文件系統(tǒng)原理

          共 5846字,需瀏覽 12分鐘

           ·

          2023-01-09 14:18

          在 Unix 的世界里,有句很經(jīng)典的話:一切對象皆是文件。這句話的意思是說,可以將 Unix 操作系統(tǒng)中所有的對象都當(dāng)成文件,然后使用操作文件的接口來操作它們。Linux 作為一個類 Unix 操作系統(tǒng),也努力實現(xiàn)這個目標(biāo)。

          虛擬文件系統(tǒng)簡介

          為了實現(xiàn)?一切對象皆是文件?這個目標(biāo),Linux 內(nèi)核提供了一個中間層:虛擬文件系統(tǒng)(Virtual File System)。

          如果大家使用過面向?qū)ο缶幊陶Z言(如C++/Java等)的話,應(yīng)該對?接口?這個概念并不陌生。而虛擬文件系統(tǒng)類似于面向?qū)ο笾械慕涌?,定義了一套標(biāo)準(zhǔn)的接口。開發(fā)者只需要實現(xiàn)這套接口,即可以使用操作文件的接口來操作對象。如下圖所示:

          5990b12d640953f17348910458bac7e6.webp

          上圖中的藍色部分就是虛擬文件系統(tǒng)所在位置。

          從上圖可以看出,虛擬文件系統(tǒng)為上層應(yīng)用提供了統(tǒng)一的接口。如果某個文件系統(tǒng)實現(xiàn)了虛擬文件系統(tǒng)的接口,那么上層應(yīng)用就能夠使用諸如?open()read()?和?write()?等函數(shù)來操作它們。

          今天,我們就來介紹虛擬文件系統(tǒng)的原理與實現(xiàn)。

          虛擬文件系統(tǒng)原理

          在闡述虛擬文件系統(tǒng)的原理前,我們先來介紹一個 Java 例子。通過這個 Java 例子,我們能夠更容易理解虛擬文件系統(tǒng)的原理。

          一個Java例子

          如果大家使用過 Java 編寫程序的話,那么就很容易理解虛擬文件系統(tǒng)了。我們使用 Java 的接口來模擬虛擬文件系統(tǒng)的定義:

              public?interface?VFSFile?{
          ??int?open(String?file,?int?mode);
          ??int?read(int?fd,?byte[]?buffer,?int?size);
          ??int?write(int?fd,?byte[]?buffer,?int?size);
          ??...
          }

          上面定義了一個名為?VFSFile?的接口,接口中定義了一些方法,如?open()read()?和?write()?等?,F(xiàn)在我們來定義一個名為?Ext3File?的對象來實現(xiàn)這個接口:

              public?class?Ext3File?implements?VFSFile?{
          ??@Override
          ??public?int?open(String?file,?int?mode)?{
          ????...
          ??}
          ??
          ??@Override
          ??public?int?read(int?fd,?byte[]?buffer,?int?size)?{
          ????...
          ??}
          ??
          ??@Override
          ??public?int?write(int?fd,?byte[]?buffer,?int?size)?{
          ????...
          ??}
          ??
          ??...
          }

          現(xiàn)在我們就能使用?VFSFile?接口來操作?Ext3File?對象了,如下代碼:

              public?class?Main()?{
          ??public?static?void?main(String[]?args)?{
          ????VFSFile?file?=?new?Ext3File();
          ????
          ????int?fd?=?file.open("/tmp/file.txt",?0);
          ????...
          ??}
          }

          從上面的例子可以看出,底層對象只需要實現(xiàn)?VFSFile?接口,就可以使用?VFSFile?接口相關(guān)的方法來操作對象,用戶完全不需要了解底層對象的實現(xiàn)過程。

          虛擬文件系統(tǒng)原理

          上面的 Java 例子已經(jīng)大概說明虛擬文件系統(tǒng)的原理,但由于 Linux 是使用 C 語言來編寫的,而 C 語言并沒有接口這個概念。所以,Linux 內(nèi)核使用了一些技巧來模擬接口這個概念。

          下面來介紹一下 Linux 內(nèi)核是如何實現(xiàn)的。

          1. file結(jié)構(gòu)

          為了模擬接口,Linux 內(nèi)核定義了一個名為?file?的結(jié)構(gòu)體,其定義如下:

              struct?file?{
          ????...
          ????const?struct?file_operations?*f_op;
          ????...
          };

          在 file 結(jié)構(gòu)中,最為重要的一個字段就是?f_op,其類型為?file_operations?結(jié)構(gòu)。而?file_operations?結(jié)構(gòu)是由一組函數(shù)指針組成,其定義如下:

              struct?file_operations?{
          ????...
          ????loff_t?(*llseek)?(struct?file?*,?loff_t,?int);
          ????ssize_t?(*read)?(struct?file?*,?char?__user?*,?size_t,?loff_t?*);
          ????ssize_t?(*write)?(struct?file?*,?const?char?__user?*,?size_t,?loff_t?*);
          ????...
          ????int?(*open)?(struct?inode?*,?struct?file?*);
          ????...
          };

          從?file_operations?結(jié)構(gòu)的定義可以隱約看到接口的影子,所以可以猜想出,如果實現(xiàn)了?file_operations?結(jié)構(gòu)中的方法,應(yīng)該就能接入到虛擬文件系統(tǒng)中。

          在 Linux 內(nèi)核中,file?結(jié)構(gòu)代表著一個被打開的文件。所以,只需要將?file?結(jié)構(gòu)的?f_op?字段設(shè)置成不同文件系統(tǒng)實現(xiàn)好的方法集,那么就能夠使用不同文件系統(tǒng)的功能。

          這個過程在?__dentry_open()?函數(shù)中實現(xiàn),如下所示:

              static?struct?file?*
          __dentry_open(struct?dentry?*dentry,?
          ??????????????struct?vfsmount?*mnt,?
          ??????????????truct?file?*f,?
          ??????????????int?(*open)(struct?inode?*,?struct?file?*),?
          ??????????????const?struct?cred?*cred)
          {

          ????...
          ????inode?=?dentry->d_inode;
          ????...
          ????//?設(shè)置file結(jié)構(gòu)的f_op字段為底層文件系統(tǒng)實現(xiàn)的方法集
          ????f->f_op?=?fops_get(inode->i_fop);
          ????...
          ????return?f;
          }

          設(shè)置好?file?結(jié)構(gòu)的?f_op?字段后,虛擬文件系統(tǒng)就能夠使用通用的接口來操作此文件了。調(diào)用過程如下:

          4639f303571c0f4e5355eddd71c080ff.webp

          2. file_operations結(jié)構(gòu)

          底層文件系統(tǒng)需要實現(xiàn)虛擬文件系統(tǒng)的接口,才能被虛擬文件系統(tǒng)使用。也就是說,底層文件系統(tǒng)需要實現(xiàn)?file_operations?結(jié)構(gòu)中的方法集。

          一般底層文件系統(tǒng)會在其內(nèi)部定義好?file_operations?結(jié)構(gòu),并且填充好其方法集中的函數(shù)指針。如?minix文件系統(tǒng)?就定義了一個名為?minix_file_operations?的?file_operations?結(jié)構(gòu)。其定義如下:

              //?文件:fs/minix/file.c

          const?struct?file_operations?minix_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,
          };

          也就是說,如果當(dāng)前使用的是 minix 文件系統(tǒng),當(dāng)使用?read()?函數(shù)讀取其文件的內(nèi)容時,那么最終將會調(diào)用?do_sync_read()?函數(shù)來讀取文件的內(nèi)容。

          3. dentry結(jié)構(gòu)

          到這里,虛擬文件系統(tǒng)的原理基本分析完畢,但還有兩個非常重要的結(jié)構(gòu)要介紹一下的:dentry?和?inode。

          dentry?結(jié)構(gòu)表示一個打開的目錄項,當(dāng)我們打開文件?/usr/local/lib/libc.so?文件時,內(nèi)核會為文件路徑中的每個目錄創(chuàng)建一個?dentry?結(jié)構(gòu)。如下圖所示:

          ad354ac663c3491927dfdddf616556d7.webp

          可以看到,file?結(jié)構(gòu)有個指向?dentry?結(jié)構(gòu)的指針,如下所示:

              struct?file?{
          ????...
          ????struct?path?f_path;
          ????...
          ????const?struct?file_operations?*f_op;
          ????...
          };

          struct?path?{
          ????...
          ????struct?dentry?*dentry;
          };

          與文件類似,目錄也有相關(guān)的操作接口,所以在?dentry?結(jié)構(gòu)中也有操作方法集,如下所示:

              struct?dentry?{
          ????...
          ????struct?dentry?*d_parent;??????????????//?父目錄指針
          ????struct?qstr?d_name;???????????????????//?目錄名字
          ????struct?inode?*d_inode;????????????????//?指向inode結(jié)構(gòu)
          ????...
          ????const?struct?dentry_operations?*d_op;?//?操作方法集
          ????...
          };

          其中的?d_op?字段就是目錄的操作方法集。

          內(nèi)核在打開文件時,會為路徑中的每個目錄創(chuàng)建一個?dentry?結(jié)構(gòu),并且使用?d_parent?字段來指向其父目錄項,這樣就能通過?d_parent?字段來追索到根目錄。

          4. inode結(jié)構(gòu)

          在 Linux 內(nèi)核中,inode?結(jié)構(gòu)表示一個真實的文件。為什么有了?dentry?結(jié)構(gòu)還需要?inode?結(jié)構(gòu)呢?這是因為 Linux 存在硬鏈接的概念。

          例如使用以下命令為?/usr/local/lib/libc.so?文件創(chuàng)建一個硬鏈接:

              ln?/usr/local/lib/libc.so?/tmp/libc.so

          現(xiàn)在?/usr/local/lib/libc.so?和?/tmp/libc.so?指向同一個文件,但它們的路徑是不一樣的。所以,就需要引入?inode?結(jié)構(gòu)了。如下圖所示:

          ba094eea9c38c0b4ab4e8e4632d6e309.webp

          由于?/usr/local/lib/libc.so?和?/tmp/libc.so?指向同一個文件,所以它們都使用同一個?inode?對象。

          inode 結(jié)構(gòu)保存了文件的所有屬性值,如文件的創(chuàng)建時間、文件所屬用戶和文件的大小等。其定義如下所示:

              struct?inode?{
          ????...
          ????uid_t???????????i_uid;???????????????//?文件所屬用戶
          ????gid_t???????????i_gid;???????????????//?文件所屬組
          ????...
          ????struct?timespec?i_atime;?????????????//?最后訪問時間
          ????struct?timespec?i_mtime;?????????????//?最后修改時間
          ????struct?timespec?i_ctime;?????????????//?文件創(chuàng)建時間
          ????...
          ????unsigned?short??i_bytes;?????????????//?文件大小
          ????...
          ????const?struct?file_operations?*i_fop;?//?文件操作方法集(用于設(shè)置file結(jié)構(gòu))
          ????...
          };

          我們注意到 inode 結(jié)構(gòu)有個類型為?file_operations?結(jié)構(gòu)的字段?i_fop,這個字段保存了文件的操作方法集。當(dāng)用戶調(diào)用?open()?系統(tǒng)調(diào)用打開文件時,內(nèi)核將會使用?inode?結(jié)構(gòu)的?i_fop?字段賦值給?file?結(jié)構(gòu)的?f_op?字段。我們再來重溫下賦值過程:

              static?struct?file?*
          __dentry_open(struct?dentry?*dentry,?
          ??????????????struct?vfsmount?*mnt,?
          ??????????????truct?file?*f,?
          ??????????????int?(*open)(struct?inode?*,?struct?file?*),?
          ??????????????const?struct?cred?*cred)
          {

          ????...
          ????//?文件對應(yīng)的inode對象
          ????inode?=?dentry->d_inode;
          ????...
          ????//?使用inode結(jié)構(gòu)的i_fop字段賦值給file結(jié)構(gòu)的f_op字段
          ????f->f_op?=?fops_get(inode->i_fop);
          ????...
          ????return?f;
          }

          總結(jié)

          本文主要介紹了?虛擬文件系統(tǒng)?的基本原理,從分析中可以發(fā)現(xiàn),虛擬文件系統(tǒng)使用了類似于面向?qū)ο缶幊陶Z言中的接口概念。正是有了?虛擬文件系統(tǒng),Linux 才能支持各種各樣的文件系統(tǒng)。


          瀏覽 72
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  操色逼免费| 日韩欧美中文在线无线码视频在线看免费版 | 69人人妻人人澡人人爽国产DVD | 三级成人在线 | 高清无码视频在线观看 |