1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        基于Fuse的最簡單的文件系統(tǒng)

        共 7034字,需瀏覽 15分鐘

         ·

        2024-04-12 00:32

        從今天開始,我打算挖一個比較大的坑。我計劃在我的公眾號“數(shù)據(jù)存儲張”創(chuàng)建一個合集,介紹如何從零開始開發(fā)一個文件系統(tǒng)。整個開發(fā)工作基于Ubuntu 20.04,當然其它Linux版本也沒有問題。對于文件系統(tǒng)的開發(fā),我們通常的理解是文件系統(tǒng)是一個內(nèi)核模塊。但是實際情況是文件系統(tǒng)并不一定是內(nèi)核模塊,比如EMC的UFS64就是一個用戶態(tài)的文件系統(tǒng)。同時,很多開源的分布式文件系統(tǒng)也都是用戶態(tài)的。
        鑒于內(nèi)核態(tài)開發(fā)門檻較高,所以我暫時不打算基于Linux內(nèi)核來開發(fā),而是借助FUSE來開發(fā)一個用戶態(tài)的文件系統(tǒng)。通過本合集的實踐,希望大家能夠?qū)Ρ救俗局段募到y(tǒng)技術(shù)內(nèi)幕》中的內(nèi)容能夠有更加深刻的理解。其實無論是內(nèi)核態(tài)的文件系統(tǒng)也好,用戶態(tài)的文件系統(tǒng)也罷,其原理是相同的。

        雖然暫時不打算基于內(nèi)核開發(fā),但基于用戶態(tài)的內(nèi)容實現(xiàn)完成后,如果大家對Linux內(nèi)核文件系統(tǒng)的實現(xiàn)感興趣,我也會繼續(xù)基于內(nèi)核實現(xiàn)一些功能,讓大家對內(nèi)核文件系統(tǒng)有一個比較全面的理解。
        好了,言歸正傳,我們回到本文的正題,繼續(xù)我們的文件系統(tǒng)之旅。在具體實現(xiàn)文件系統(tǒng)之前,我覺得有必要介紹一些基本概念,這樣大家對理解后續(xù)內(nèi)容會有所幫助。

        什么是文件系統(tǒng)


        首先我們介紹一下什么是文件系統(tǒng)。

        文件系統(tǒng)是一個將硬盤的線性地址轉(zhuǎn)換為層級結(jié)構(gòu)的軟件系統(tǒng),其核心是給用戶呈現(xiàn)層級結(jié)構(gòu)的目錄樹(如下圖所示)。在文件系統(tǒng)中有兩個非常重要的概念,一個是文件,另外一個是目錄(或者文件夾)。其中目錄是一個容器,可以存儲文件或者目錄(稱為子目錄)。文件是存儲數(shù)據(jù)的實體,我們的數(shù)據(jù)都是以文件的形態(tài)進行存儲的。文件有很多種類,比如視頻文件、音頻文件、Word文檔和文本文件等等。


        文件系統(tǒng)的API


        在普通用戶角度,文件系統(tǒng)提供了一個層級結(jié)構(gòu)的文件組織方式。

        從程序開發(fā)的角度,文件系統(tǒng)提供一套API來訪問文件和目錄。比如文件的打開、關(guān)閉、讀取數(shù)據(jù)和寫入數(shù)據(jù)等,目錄的打開、讀?。ū闅v)和關(guān)閉等等,具體如下所示是這些API的一個子集。對于上述API,在內(nèi)核態(tài)都有對應(yīng)的API來實現(xiàn)具體的功能。

        理解這些API很重要,因為FUSE正是在內(nèi)核態(tài)對這些API的請求進行了截獲,然后轉(zhuǎn)發(fā)到用戶態(tài)來處理的。

        功能描述 Linux API
        打開文件 open
        向文件寫數(shù)據(jù) write
        從文件讀數(shù)據(jù) read
        關(guān)閉文件 close
        移動文件指針位置 lseek
        獲取文件屬性 stat
        遍歷目錄 readdir

        什么是FUSE


        FUSE實現(xiàn)了一個在用戶態(tài)開發(fā)文件系統(tǒng)的框架,有了這個框架,我們可以在用戶態(tài)開發(fā)文件系統(tǒng)的邏輯,而不用關(guān)心Linux內(nèi)核的相關(guān)內(nèi)容。從而大大降低了開發(fā)文件系統(tǒng)的門檻。
        FUSE本身包含一個用戶態(tài)的庫和一個內(nèi)核模塊。內(nèi)核態(tài)模塊與VFS及其他文件系統(tǒng)的關(guān)系如圖所示,可以理解FUSE為一個內(nèi)核態(tài)的文件系統(tǒng)。內(nèi)核態(tài)的文件系統(tǒng),并不會將數(shù)據(jù)持久化,其功能是將文件系統(tǒng)訪問請求轉(zhuǎn)發(fā)到用戶態(tài)。
        用戶態(tài)庫提供了一套API,同時提供了一套接口規(guī)范,這套規(guī)范實際上是一組函數(shù)集合?;贔USE開發(fā)文件系統(tǒng)就是實現(xiàn)FUSE定義的函數(shù)集合的某些或者全部函數(shù)。然后調(diào)用FUSE用戶態(tài)庫的API將實現(xiàn)的函數(shù)注冊到內(nèi)核模塊中。在下圖的示例中,ceph_fuse就是基于FUSE開發(fā)的一個用戶態(tài)的文件系統(tǒng),用于實現(xiàn)對CephFS的訪問的。如果我們基于FUSE開發(fā)自己的文件系統(tǒng),也是位于這個位置的。



        前文已述,內(nèi)核態(tài)的模塊基于VFS實現(xiàn)了一個文件系統(tǒng),可以與Ext4、NFS或者CephFS主機端的文件系統(tǒng)對比理解。但不同的是,當有用戶請求達到該文件系統(tǒng)時,該文件系統(tǒng)不是訪問磁盤或者是通過網(wǎng)絡(luò)發(fā)送請求,而是調(diào)用用戶態(tài)注冊的回調(diào)函數(shù)。
        如果大家對前文NFS的流程,或者CephFS的流程熟悉的話,理解FUSE工作原理就非常簡單了。與NFS類比,F(xiàn)USE將NFS中通過網(wǎng)絡(luò)轉(zhuǎn)化請求換成了通過函數(shù)調(diào)用(嚴格來說并非簡單的函數(shù)調(diào)用,因為涉及內(nèi)核態(tài)到用戶態(tài)的轉(zhuǎn)換)來轉(zhuǎn)化請求。

        實現(xiàn)一個不是文件系統(tǒng)的文件系統(tǒng)


        今天我們將開發(fā)一個最簡單的文件系統(tǒng),嚴格來說都不是一個文件系統(tǒng)。這里我們只是模擬一個文件系統(tǒng)的層級結(jié)構(gòu)。在這個文件系統(tǒng)中包含一個目錄和一個文件,目錄的名稱為“dir”,文件的名稱為“helloworld”。同時,按照Linux的慣例,這里還包含當前目錄“.”和上一級目錄“..”。該文件系統(tǒng)的目錄結(jié)構(gòu)如下圖所示。


        雖然這里是一個層級結(jié)構(gòu),但是這個文件系統(tǒng)并不能像其他文件系統(tǒng)那樣在目錄中創(chuàng)建文件,也不能刪除文件,更不能讀取文件的內(nèi)容。為什么?原因很簡單,因為我們并沒有實現(xiàn)上述功能。
        接下來我們來看一下這個文件系統(tǒng)的具體實現(xiàn)。所有文件都在一個名稱為helloworld的目錄當中,該目錄中的文件與作用如下圖所示。其中3個文件(Fuse.cpp、Fuse.h和Fuse-impl.h)是FUSE的C++封裝,這里我們借用了James的實現(xiàn),感謝他這方面的工作。另外3個C++源代碼文件是本文件系統(tǒng)的實現(xiàn),其中helloworld.cpp是主函數(shù)的源文件,其實沒有太多內(nèi)容,helloworldFS.cpp/h是文件系統(tǒng)的實現(xiàn)。CMakeLists.txt是cmake的源文件,用于管理整個工程。


        如下是文件系統(tǒng)的具體實現(xiàn),可以看到這里只實現(xiàn)了2個函數(shù),分別是getattr和readdir。其中readdir用于讀取目錄項,getattr用于獲取文件或者目錄的詳細屬性。這兩個函數(shù)正是命令行工具ls會調(diào)用到的命令。
        /* Description: HelloWorld filesystem class implementation
        * This class implements the core API that is related to the directory and file.
        * In this class, we implement a very simple file system.
        */


        #include "helloworldFS.h"

        #include <iostream>
        #include <string>

        // include in one .cpp file
        #include "Fuse-impl.h"

        using namespace std;

        static const string root_path = "/";
        static const string hello_str = "Data Storage Zhang!\n";
        static const string hello_path = "/helloworld";
        static const string dir_path = "/dir";

        int HelloWorldFS::getattr(const char *path, struct stat *stbuf, struct fuse_file_info *)
        {
        int res = 0;

        memset(stbuf, 0, sizeof(struct stat));
        if (path == root_path) { //根目錄的屬性
        stbuf->st_mode = S_IFDIR | 0755;
        stbuf->st_nlink = 2;
        } else if (path == hello_path) { //helloworld文件的屬性
        stbuf->st_mode = S_IFREG | 0444;
        stbuf->st_nlink = 1;
        stbuf->st_size = hello_str.length();
        } else if (path == dir_path) { //dir目錄的屬性
        stbuf->st_mode = S_IFDIR | 0755;
        stbuf->st_nlink = 2;
        } else
        res = -ENOENT;

        return res;
        }

        int HelloWorldFS::readdir(const char *path, void *buf, fuse_fill_dir_t filler,
        off_t, struct fuse_file_info *, enum fuse_readdir_flags)
        {
        if (path != root_path)
        return -ENOENT;

        filler(buf, ".", NULL, 0, FUSE_FILL_DIR_PLUS);
        filler(buf, "..", NULL, 0, FUSE_FILL_DIR_PLUS);
        filler(buf, hello_path.c_str() + 1, NULL, 0, FUSE_FILL_DIR_PLUS);
        filler(buf, dir_path.c_str() + 1, NULL, 0, FUSE_FILL_DIR_PLUS);

        return 0;
        }
        頭文件的實現(xiàn)更加簡單,如下是頭文件的源代碼。在頭文件中只是定義了一個文件系統(tǒng)類HelloWorldFS,并定義了上述兩個函數(shù)。
        // Hello filesystem class definition
        #ifndef __HELLOFS_H_
        #define __HELLOFS_H_

        #include "Fuse.h"
        #include "Fuse-impl.h"

        class HelloWorldFS : public Fusepp::Fuse<HelloWorldFS>
        {
        public:
        HelloFS() {}
        ~HelloFS() {}

        static int getattr (const char *, struct stat *, struct fuse_file_info *);
        static int readdir(const char *path, void *buf,
        fuse_fill_dir_t filler,
        off_t offset, struct fuse_file_info *fi,
        enum fuse_readdir_flags);
        };

        #endif
        如下是主函數(shù)所在源文件的內(nèi)容,實現(xiàn)也是比較簡單的。這里實例化了HelloWorldFS類,并調(diào)用run函數(shù)。這個函數(shù)完成了文件系統(tǒng)掛載,函數(shù)注冊等任務(wù)。具體涉及FUSE實現(xiàn)的內(nèi)容,我們后面再詳細介紹這方面的內(nèi)容。
        // See  FUSE:  example/hello.c

        #include "helloworldFS.h"

        int main(int argc, char *argv[])
        {

        HelloWorldFS fs;

        int status = fs.run(argc, argv);

        return status;
        }
        如下是cmake的工程文件,用于生成Makefile文件。關(guān)于cmake的概念及使用方法可以閱讀本號之前的文章,本文不再贅述相關(guān)內(nèi)容。
        cmake_minimum_required(VERSION 3.16)
        SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++14 -D_FILE_OFFSET_BITS=64 -D__coverage -I/usr/include/fuse3 -lfuse3 -lpthread")
        project(CMakeSunny
        VERSION 1.0
        DESCRIPTION "A CMake Tutorial"
        LANGUAGES CXX)

        add_executable(helloworld
        helloworld.cpp
        helloworldFS.cpp
        Fuse.cpp)

        target_link_libraries(helloworld -lfuse3)
        具備上述源文件后就可以編譯源文件了。首先需要在根目錄創(chuàng)建一個名稱為build子目錄。然后切換到該目錄執(zhí)行“cmake ..”命令。成功后會生成一個Makefile。最后,我們執(zhí)行一下make命令就會編譯出一個可執(zhí)行程序helloworld。執(zhí)行如下命令就可以將我們實現(xiàn)的“文件系統(tǒng)”掛載到/mnt/test(需要提前創(chuàng)建)目錄了。
        ./helloworld /mnt/test
        然后我們可以通過cd命令切換到該目錄,并通過ls命令查看其中的內(nèi)容。這時就可以看到我們在文章一開始看到的內(nèi)容。


        如前文所述,本文我們實現(xiàn)了一個假的文件系統(tǒng),這個文件系統(tǒng)既不能創(chuàng)建新文件,也不能讀取文件的數(shù)據(jù),更不能向已有文件寫入數(shù)據(jù)。但是通過本文,相信大家對文件系統(tǒng)的概念和FUSE的用法有了一個基本的了解。后面我們將以當前實現(xiàn)的文件系統(tǒng)為基礎(chǔ),實現(xiàn)一個基于內(nèi)存的,可讀寫的文件系統(tǒng)。

        注:本文配套的源代碼可以在github的SunnyZhang-IT/fs-from-zero庫中找到。


        作者著作


        發(fā)布:劉恩惠

        審核:陳歆懿

            
              
         


        如果喜歡本文
        歡迎 在看留言分享至朋友圈 三連
        <
         PAST · 往期回顧 
        >

        書單 | 3月新書速遞!


        瀏覽 86
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

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

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            亚洲高清无码电影 | 大鸡巴插逼逼 | 小sao货叫开腿我cao死你 | 女同xcream唾液接吻舌头 | 天天日天天插天天操 | 久久精品免费视频性生活 | 豆花无码一区二区三区 | 扒开腿做爽爽爽 | 国产夫妻性生活勉费视频 | 日本一曲二曲三曲小电影 |