美文网首页
基于dokan的内存文件系统实现

基于dokan的内存文件系统实现

作者: 大海无垠_af22 | 来源:发表于2019-11-30 10:44 被阅读0次

    dokan是windows系统上的FUSE实现,基于它提供的接口,可以很容易的实现一个用户态文件系统,挂载到系统成为一个分区,使用上就和本地硬盘一样。
    本文基于dokan实现了一个内存型文件系统,使用内存存储数据,读写速度极快,当然缺点也很多,最重要的就是数据不能持久化,卸载之后数据全部消失,另外简化了实现,没有目录相关操作,也就是说只支持一级目录及多个文件的模式,文件系统大小受限于本机内存大小。

    1.安装dokan

    本文使用的系统是win10家庭中文版1809,dokan版本1.3.0,下载DokanSetup.exe安装dokan。
    安装时可能遇到驱动无法注册的问题,需要使用管理员打开powershell,进入dokany目录下执行命令:

    powershell.exe -executionpolicy remotesigned -File .\cert\dokan-import.ps1
    

    完成后继续安装驱动:

    dokanctl.exe /i d
    

    2.编译环境

    本文采用cygwin编译,编译工具g++。

    3.内存文件系统代码

    本文基于dokan提供的fuse_mirror框架实现文件系统。dokan提供windows平台的接口,为了简化开发难度和照顾linux的编程习惯,dokan又提供了fuse_mirror框架,用户只需要实现少部分文件接口,即可运行一个文件系统了。
    简单解释一下本文实现的内存文件系统实现原理,本文件系统仅需考虑多个文件即可,实现创建、删除、读写、重命名功能。每个文件由多个1M的数据块组成,在实现上数据块用string结构,文件采用map结构组织多个数据块,键值是数据块编号。文件系统采用map结构的files存储多个文件,files作用是将文件名映射到文件结构。创建文件主要实现是检查文件是否已存在,不存在则往files里插入新的文件结构。删除实现则从files里删除文件,同时扣除文件系统空间使用量。重命名主要是采用move语义将文件内容转移,从files删除原有文件名,然后插入新的文件。读写实现比较复杂,读文件时根据指定的偏移计算对应数据块编号,然后从数据块从复制数据到用户缓冲区,难点是需要考虑读取范围超出文件大小、跨多个数据块读取、读取不足一个数据块等多种情形,为了简化难度,这里没有考虑稀疏文件的支持。写文件过程类似,当超出文件大小范围时则创建新的数据块,新建的数据块预申请1M内存,写完之后更新文件系统空间使用量。

    废话不多说,来看代码,本代码基于fusexmp.cpp修改,文件名fusexmp-mem.cpp。

    #define FUSE_USE_VERSION 30
    
    #ifdef HAVE_CONFIG_H
    //#include <config.h>
    #endif
    
    #ifdef linux
    /* For pread()/pwrite()/utimensat() */
    #define _XOPEN_SOURCE 700
    #endif
    
    #include <stdio.h>
    #include <string.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <sys/stat.h>
    #include <dirent.h>
    #include <errno.h>
    #include <sys/time.h>
    #ifdef HAVE_SETXATTR
    #include <sys/xattr.h>
    #endif
    #include <iostream>
    #include <map>
    #include <vector>
    #include <string>
    #include <mutex>
    #include <thread>
    #include <assert.h>
    
    #include "fuse.h"
    
    using namespace std;
    #define LOG debug && printf
    #define BLK_SIZE (1<<20)
    #define blk_at(off) ((int)((off) / BLK_SIZE))
    #define blk_off(off) ((int)((off) & (BLK_SIZE - 1)))
    
    const static int MAX_FS_KB = 8*1024*1024;
    const static int MAX_FILE_NUM = 100;
    const static int MAX_FILE_NAME_LEN = 100;
    static bool debug = false;
    
    class Log{
    private:
        const char* func;
        const char* path;
        int& res;
    public:
        Log(const char*f, const char*p, int& r): res(r){
            func = f;
            path = p;
            LOG("%s: %s\n", func, path) && fflush(stderr);
        }
        ~Log(){
            if(!debug)
                return;
            if(res>=0)
                cout << func << ": " << path << " ok " << res << endl;
            else
                cout << func << ": " << path << " errno " << -res << " " << strerror(-res) << endl;
        }
    };
    
    template<class X>
    class Log2{
    private:
        const char* func;
        const char* path;
        const X& x;
        int& res;
    public:
        Log2(const char*f, const char*p, const X& x_, int& r): x(x_), res(r){
            func = f;
            path = p;
            if(!debug)
                return;
            cout << func << ": " << path << " " << x<< endl;
            //fflush(stdout);
        }
        ~Log2(){
            if(!debug)
                return;
            if(res>=0)
                cout << func << ": " << path << " " << x << " ok " << res << endl;
            else
                cout << func << ": " << path << " " << x << " errno " << -res << " " << strerror(-res) << endl;
            //fflush(stdout);
        }
    };
    
    struct myfile{
        int ino;
        off_t size;
        map<int, string> content;
        mutex mtx;
        
        myfile(){
            ino = 0;
            size = 0;
        }
        
        myfile(myfile&& m):ino(m.ino), size(m.size), 
        content(move(m.content)){}
        
        int do_read(const char *path, char *buf, size_t size, off_t offset);
        int do_write(const char *path, const char *buf, size_t size, off_t offset);
    };
    
    
    struct myfs{
        // filename -> file
        // test.txt -> file
        map<string, myfile> files;
        volatile size_t used_kb;
        volatile size_t total_kb;
        int max_files;
    };
    
    static mutex mtx;
    static myfs fs;
    
    
    int myfile::do_read(const char *path, char *buf, size_t size, off_t offset){
        int res = 0;
        if(offset < 0)
            offset = 0;
        if(offset >= this->size){
            LOG("%s: %s off %lld >= size %lld\n", __func__, path, offset, this->size);
            return res;
        }
        if(offset + size > this->size){
            size = this->size - offset;
            LOG("%s: %s off %lld size %lld max %lld\n", __func__, path, offset, this->size, size);
        }
        res = size;
        unique_lock<mutex> lck(this->mtx);
        while(size > 0){
            int b = blk_at(offset);
            int s = blk_off(offset);
            const string& ct = this->content[b];
            LOG("read blk %d off %d blk size %lld\n", b, s, ct.size());
            if(s+size <= ct.size()){
                memcpy(buf, &ct[s], size);
                offset += size;
                LOG("read %lld bytes from blk %d off %d\n", size, b, s);
                break;
            } else {
                size_t r = ct.size() - s;
                memcpy(buf, ct.c_str() + s, r);
                LOG("read %d bytes from blk %d off %d\n", r, b, s);
                buf += r;
                size -= r;
                offset += r;
            }
        }
        return res;
    }
    int myfile::do_write(const char *path, const char *buf, size_t size, off_t offset){
        int res = 0;
        {
            if(offset < 0)
                offset = 0;
            unique_lock<mutex> lck(mtx);
            size_t old_size_kb = (1023 + this->size) >> 10;
            size_t new_size_kb = (1023 + offset + size) >> 10;
            if(new_size_kb > old_size_kb){
                if(fs.total_kb <= fs.used_kb){
                    fprintf(stderr, "error: no space used %lld/%lld\n", fs.used_kb, fs.total_kb);
                    res = -EACCES;
                    return res;
                }
                if(new_size_kb - old_size_kb + fs.used_kb > fs.total_kb){
                    size = 1024*(fs.total_kb - fs.used_kb);
                    new_size_kb = (1023 + offset + size) >> 10;
                    if(new_size_kb > old_size_kb)
                        fs.used_kb += new_size_kb - old_size_kb;
                } else {
                    fs.used_kb += new_size_kb - old_size_kb;
                }
            }
        }
        res = size;
        unique_lock<mutex> lck(this->mtx);
        while(size > 0){
            int b = blk_at(offset);
            int s = blk_off(offset);
            string& ct = this->content[b];
            if(ct.empty()){
                LOG("new block at %d\n", b);
                ct.reserve(BLK_SIZE);
            }
            size_t r = s + size <= BLK_SIZE ? size : BLK_SIZE - s;
            const char* t = buf + r;
            size -= r;
            offset += r;
            assert(s <= ct.size());
            if(s + r <= ct.size()){
                memcpy(&ct[s], buf, r);
            } else {
                size_t cpy = ct.size() - s;
                r -= cpy;
                memcpy(&ct[s], buf, cpy);
                buf += cpy;
                ct.append(buf, r);
            }
            buf = t;
        }
        if(offset > this->size)
            this->size = offset;
        return res;
    }
    
    
    myfile* findfile(const char*name){
        auto ptr = fs.files.find(name);
        if(ptr == fs.files.end())
            return NULL;
        return &(ptr->second);
    }
    
    static int xmp_getattr(const char *path, struct stat *stbuf)
    {
        unique_lock<mutex> lck(mtx);
        int res = 0;
    
        Log log(__func__, path, res);
        if(path[1] == '\0'){
            memset(stbuf, 0, sizeof *stbuf);
            stbuf->st_ino = 1;
            stbuf->st_mode = S_IFDIR|0777;
            stbuf->st_nlink = 1;
            stbuf->st_uid = 0;
            stbuf->st_size = fs.used_kb * 1024;
            stbuf->st_blksize = 512;
            stbuf->st_blocks = (stbuf->st_size + 511) & ~511;
            return 0;
        }
        string name(path+1);
        auto ptr = fs.files.find(name);
        if(fs.files.end() == ptr){
            res = -ENOENT;
            return res;
        }
        myfile& mf = ptr->second;
        stbuf->st_ino = mf.ino;
        stbuf->st_mode = 0666;
        stbuf->st_nlink = 1;
        stbuf->st_uid = 0;
        stbuf->st_size = mf.size;
        stbuf->st_blksize = 512;
        stbuf->st_blocks = (mf.size + 511) & ~511;
        
        return 0;
    }
    
    static int xmp_create(const char *path, mode_t mode, struct fuse_file_info *fi){
        int res = 0;
        Log log(__func__, path, res);
        unique_lock<mutex> lck(mtx);
        if(!path[1]){
            res = -EACCES ;
            return res;
        }
        if(fs.files.size() == MAX_FILE_NUM){
            LOG("%s: error no space\n", __func__);
            res = -EACCES ;
            return res;
        }
        ++path;
        if(NULL!=findfile(path)){
            res = -EEXIST;
            return res;
        }
        if(strlen(path) > MAX_FILE_NAME_LEN - 1){
            LOG("%s: error name too long\n", __func__);
            res = -EACCES ;
            return res;
        }
        myfile& m = fs.files[string(path)];
        m.ino = fs.max_files + 1;
        m.size = 0;
        fi->fh = (off_t)(&m);
        return res;
    }
    
    static int xmp_open(const char *path, struct fuse_file_info *fi)
    {
        int res=0;
        Log2 log(__func__, path, fi->flags, res);
        if(!path[1]){
            res = -EACCES ;
            return res;
        }
        ++path;
        myfile* pf;
        unique_lock<mutex> lck(mtx);
        if(!(pf=findfile(path))){
            if((fi->flags & (O_WRONLY|O_CREAT)) == (O_WRONLY|O_CREAT)){
                if(fs.files.size() == MAX_FILE_NUM){
                    LOG("%s: error no space\n", __func__);
                    res = -EACCES;
                    return res;
                }
                if(strlen(path) > MAX_FILE_NAME_LEN - 1){
                    LOG("%s: error name too long\n", __func__);
                    res = -EACCES ;
                    return res;
                }
                myfile& m = fs.files[string(path)];
                m.ino = fs.max_files + 1;
                m.size = 0;
                fi->fh = (off_t)(&m);
                return res;
            }
            res = -ENOENT;
            return res;
        }
        fi->fh = (off_t)(pf);
        return 0;
    }
    
    static int xmp_read(const char *path, char *buf, size_t size, off_t offset,
                struct fuse_file_info *fi)
    {
        int res = 0;
        Log2 log(__func__, path, offset , res);
        myfile* pf = (myfile*)fi->fh;
        if(!pf){
            res = -ENOENT;
            return res;
        }
        res = pf->do_read(path, buf, size, offset);
        return res;
    }
    
    static int xmp_write(const char *path, const char *buf, size_t size,
                 off_t offset, struct fuse_file_info *fi)
    {
        int res = 0;
        Log2 log(__func__, path, size, res);
        myfile* pf = (myfile*)fi->fh;
        if(!pf){
            res = -ENOENT;
            return res;
        }
        res = pf->do_write(path, buf, size, offset);
        return res;
    }
    
    static int xmp_rename(const char *from, const char *to)
    {
        int res = 0;
        Log2 log(__func__, from, to, res);
        if(!from[1] || !to[1]){
            res = -EACCES ;
            return res;
        }
        ++from;
        ++to;
        myfile* pf;
        unique_lock<mutex> lck(mtx);
        if(!(pf=findfile(from))){
            res = -ENOENT;
            return res;
        }
        if(NULL!=findfile(to)){
            res = -EEXIST;
            return res;
        }
        fs.files.emplace(to, myfile(move(*(&fs.files[from]))));
        fs.files.erase(from);
        return res;
    }
    
    
    static int xmp_readdir(const char * path, void * buf,
                           fuse_fill_dir_t fill, FUSE_OFF_T off,
                           struct fuse_file_info *fi){
        int res = 0;
        Log log(__func__, path, res);
        map<string, size_t> tmp_files;
        size_t used_kb=0;
        {
            unique_lock<mutex> lck(mtx);
            if(path[1]){
                res = -ENOENT;
                return res;
            }
            used_kb = fs.used_kb;
            for(auto& ptr: fs.files){
                tmp_files[ptr.first] = ptr.second.size;
            }
        }
        struct stat stbuf={0};
        //stbuf.st_ino = 1;
        stbuf.st_mode = S_IFDIR|0777;
        stbuf.st_nlink = 1;
        stbuf.st_uid = 0;
        stbuf.st_size = used_kb * 1024;
        stbuf.st_blksize = 512;
        stbuf.st_blocks = (stbuf.st_size + 511) & ~511;
        char m[2] = ".";
        char f[3] = "..";
       if(fill(buf, m, &stbuf, 0))
           return 0;
       if(fill(buf, f, &stbuf, 0))
           return 0;
        stbuf.st_mode = 0666;
        stbuf.st_nlink = 1;
        stbuf.st_uid = 0;
        stbuf.st_blksize = 512;
        char name[MAX_FILE_NAME_LEN];
        for(auto& ptr: tmp_files){
            const string& name_s = ptr.first;
            strcpy(name, name_s.c_str());
            stbuf.st_size = ptr.second;
            stbuf.st_blocks = (stbuf.st_size + 511) & ~511;
            LOG("%s: name %s size %lld\n", __func__, name, stbuf.st_size);
           if(fill(buf, name, &stbuf, 0))
               break;
        }
        
       return 0;
    }
    
    
    static int xmp_opendir(const char * path, struct fuse_file_info * info){
        if(path[1])
            return  -ENOENT;
        return 0;
    }
    
    static int xmp_unlink(const char *path)
    {
        int res = 0;
        Log log(__func__, path, res);
        if(!path[1]){
            res = -EACCES;
            return res;
        }
        ++path;
        unique_lock<mutex> lck(mtx);
        myfile* pf;
        if(!(pf=findfile(path))){
            res = -ENOENT;
            return res;
        }
        size_t size_kb = (1023 + pf->size) >> 10;
        fs.used_kb -= size_kb;
        fs.files.erase(path);
        return res;
    }
    
    
    static int xmp_statfs(const char *path, struct statvfs *stbuf)
    {
        int res = 0;
        Log log(__func__, path, res);
        unique_lock<mutex> lck(mtx);
        stbuf->f_bsize = 512;
        stbuf->f_bavail = (fs.total_kb - fs.used_kb) * 2;
        stbuf->f_bfree = stbuf->f_bavail;
        stbuf->f_blocks = fs.total_kb * 2;
        return 0;
    }
    
    static int xmp_access(const char *path, int mask)
    {
        int res;
        return 0;
    }
    
    static int xmp_readlink(const char *path, char *buf, size_t size)
    {
        int res = -EINVAL;
        Log log(__func__, path, res);
        return res;
    }
    
    static int xmp_mknod(const char *path, mode_t mode, dev_t rdev)
    {
        int res = -EINVAL;
        Log log(__func__, path, res);
        return res;
    }
    
    static int xmp_mkdir(const char *path, mode_t mode)
    {
        return -EACCES ;
    }
    
    static int xmp_rmdir(const char *path)
    {
        return -EACCES ;
    }
    
    static int xmp_symlink(const char *from, const char *to)
    {
        return -EINVAL;
    }
    
    static int xmp_link(const char *from, const char *to)
    {
        return -EINVAL;
    }
    
    static int xmp_chmod(const char *path, mode_t mode)
    {
        return -EINVAL;
    }
    
    static int xmp_chown(const char *path, uid_t uid, gid_t gid)
    {
        return -EINVAL;
    }
    
    static int xmp_truncate(const char *path, off_t size)
    {
        return -EINVAL;
    }
    
    
    static int xmp_release(const char *path, struct fuse_file_info *fi)
    {
        return 0;
    }
    
    static int xmp_fsync(const char *path, int isdatasync,
                 struct fuse_file_info *fi)
    {
        /* Just a stub.  This method is optional and can safely be left
           unimplemented */
    
        (void) path;
        (void) isdatasync;
        (void) fi;
        return 0;
    }
    
    static int xmp_fallocate(const char *path, int mode,
                off_t offset, off_t length, struct fuse_file_info *fi)
    {
        int fd;
        int res;
    
        (void) fi;
    
        return -EOPNOTSUPP;
    }
    
    /* xattr operations are optional and can safely be left unimplemented */
    static int xmp_setxattr(const char *path, const char *name, const char *value,
                size_t size, int flags)
    {
        return 0;
    }
    
    static int xmp_getxattr(const char *path, const char *name, char *value,
                size_t size)
    {
        return 0;
    }
    
    static int xmp_listxattr(const char *path, char *list, size_t size)
    {
        return 0;
    }
    
    static int xmp_removexattr(const char *path, const char *name)
    {
        return 0;
    }
    
    static  int xmp_releasedir(const char *, struct fuse_file_info *){
        return 0;
    }
    
    inline double getnow(){
        struct timeval tv;
        gettimeofday(&tv, NULL);
        return tv.tv_sec + 0.000001 * tv.tv_usec;
    }
    
    // for *pret
    #define RET_NONE 0
    #define RET_STARTED 1
    #define RET_OK 2
    #define RET_ERROR 3
    
    
    struct BenchArgs{
    // for op_file
        const static int READ_TEST = 1;
        const static int WRITE_TEST = 2;
        const static int WRITE_READ_TEST = READ_TEST | WRITE_TEST;
        int num;
        int size_gb;
        int run_num;
        
        BenchArgs(int num_, int size_gb_, int run_num_){
            num = num_;
            size_gb = size_gb_;
            run_num = run_num_;
        }
        // read or write file thread
        static void _test_read_write_thread(BenchArgs* pbench, int id, int op_file, int64_t* completed, int* pret);
        
        // multiple files test
        void _cmd_testreadwrite(int op_file);
        void do_bench();
    };
    
    void BenchArgs::_test_read_write_thread(BenchArgs* pbench, int id, 
        int op_file, int64_t* completed, int* pret)
    {
        *pret = RET_STARTED;
        char name[MAX_FILE_NAME_LEN];
        sprintf(name, "/pbench_%d", id);
        *completed = 0;
        int fd, ret;
        size_t buf_len = 1024*1024;
        char *buf = new char[buf_len];
        size_t total_bytes = 1024LL * 1024 * 1024 * pbench->size_gb;
        size_t op_num = total_bytes/buf_len;
        int64_t wr = 0;
        int read_file = op_file == READ_TEST;
        struct fuse_file_info fi = {0};
    
        if(read_file)
            op_num = 1024*1024;
        if(op_file == READ_TEST)
            fd = xmp_open(name, &fi);
        else {
            fi.flags = O_WRONLY|O_CREAT;
            fd = xmp_open(name, &fi);
        }
        if(fd < 0){
            fprintf(stderr, "open [%s] failed ret [%d]\n", name, fd);
            *pret = RET_ERROR;
            return;
        }
        memset(buf, '\0', buf_len);
        fprintf(stderr, "%d test on [%s]\n", id, name);
        for(int i=0; i < op_num; ++i){
            int r;
            if(read_file)
                r = xmp_read(name, buf, buf_len, wr, &fi);
            else
                r = xmp_write(name, buf, buf_len, wr, &fi);
            if(buf_len != r){
                if(read_file && r < buf_len){
                    break;
                }
                fprintf(stderr, "read or write on remote file [%s] failed ret [%d]\n", name, r);
                *pret = RET_ERROR;
                break;
            }
            wr += r;
            *completed = wr;
            LOG("process %d wrote %lldB\n", id, wr);
        }
        LOG("try to close file\n");
        xmp_release(name, &fi);
        fprintf(stderr, "%d ok to close file %s\n", id, name);
        if(*pret == RET_STARTED)
            *pret = RET_OK;
        delete[] buf;
    }
    
    void BenchArgs::_cmd_testreadwrite(int op_file)
    {
        std::thread workers[num];
        int64_t completed[num];
        int rets[num];
        for(int k=0;k<num; ++k){
            rets[k] = RET_NONE;
            workers[k]= std::thread(_test_read_write_thread, this, k, op_file, &completed[k], &rets[k]);
        }
        fprintf(stderr, "start to %s %d files with size %dGB\n", 
            op_file&READ_TEST? "read" : "write", num, size_gb);
        // test
        int64_t lastwr = 0;
        int k=0;
        double st = getnow();
        for(k=1; ;++k){
            sleep(1);
            int dead = 0;
            int64_t wr = 0;
            for(int j=0; j< num; ++j){
                int r = rets[j];
                wr += completed[j];
                if(r > RET_STARTED){
                    ++dead;
                    continue;
                }
            }
            wr  = wr >> 20;
            int avg = (int)(wr/(getnow() - st));
            fprintf(stderr, "%d: %lldMB %lldMB/s %dMB/s\n", k, wr, wr - lastwr, avg);
            fflush(stderr);
            lastwr = wr;
            if(dead == num){
                break;
            }
        }
        fprintf(stderr, "Avg: %ds, %lldMB, %lldMB/s\n", k, lastwr, (int)(lastwr/(getnow() - st)));
        // wait for workers
        for(int k=0;k<num; ++k){
            workers[k].join();
        }
    }
    
    void BenchArgs::do_bench()
    {
        for(int k=0; k < run_num; ++k){
            if(k > 0){
                fprintf(stderr, "\nrun %d test\n", k+1);
            }
            _cmd_testreadwrite(WRITE_TEST);
            _cmd_testreadwrite(READ_TEST);
        }
    }
    
    static char usage[] = "usage: ./xmp-mem mountpoint [options]\n"
                   "\n"
                   "general options:\n"
                   "    -o opt,[opt...]        mount options\n"
                   "    -h   --help            print help\n"
                   "    -V   --version         print version\n"
                   "    -g                     debug xmp-mem\n"
                   "    -m {memory_gb}         max memory in gb, default is 8, min is 1\n"
                   "    --bench [num] [size_gb] [run_num]\n"
                   "                           num: files num, default is 4\n"
                   "                           size_gb: every file size in GB, default is 1\n"
                   "                           run_num: test num\n"
                   "\n"
                   "    -o umask=M             set file and directory permissions (octal)\n"
                   "    -o fileumask=M         set file permissions (octal)\n"
                   "    -o dirumask=M          set directory permissions (octal)\n"
                   "    -o fsname=M            set filesystem name\n"
                   "    -o volname=M           set volume name\n"
                   "    -o uncname=M           set UNC name\n"
                   "    -o setsignals=M        set signal usage (1 to use)\n"
                   "    -o daemon_timeout=M    set timeout in seconds\n"
                   "    -o alloc_unit_size=M   set allocation unit size\n"
                   "    -o sector_size=M       set sector size\n"
                   "    -n                     use network drive\n"
    ;
    
    /*
     * prog
    usage: ./xmp-mem mountpoint [options]
    
    */
    int main(int argc, char *argv[])
    {
        fs.max_files = MAX_FILE_NUM;
        fs.used_kb = 0;
        fs.total_kb = MAX_FS_KB;
        char* newargv[100];
        int n = 0;
        BenchArgs* pbench=nullptr;
        
        newargv[n++] = argv[0];
        newargv[n++] = "-f";
        for(int k=1; k < argc; ++k){
            if(0==strcmp("-f", argv[k])){
                continue;
            } else if(0==strcmp("--bench", argv[k])){
                int num = 1;
                int gb = 1;
                int run_num = 1;
                if(++k < argc){
                    if(1!=sscanf(argv[k], "%d", &num) || num < 1){
                        printf("error: invalid bench file num\n%s", usage);
                        return 0;
                    }
                }
                if(++k < argc){
                    if(1!=sscanf(argv[k], "%d", &gb) || gb < 1){
                        printf("error: invalid bench file size\n%s", usage);
                        return 0;
                    }
                }
                if(++k < argc){
                    if(1!=sscanf(argv[k], "%d", &run_num) || run_num < 1){
                        printf("error: invalid bench test num\n%s", usage);
                        return 0;
                    }
                }
                pbench = new BenchArgs(num, gb, run_num);
            } else if(0==strcmp("-g", argv[k])){
                debug = true;
            } else if(0==strcmp("-m", argv[k])){
                int gb = 1;
                if(1==sscanf(argv[k+1], "%d", &gb))
                    fs.total_kb = gb << 20;
                ++k;
            } else if(0==strcmp("-h", argv[k])){
                printf("sizeof size_t %d\n", sizeof(size_t));
                printf("%s", usage);
                return 0;
            } else {
                newargv[n++] = argv[k];
            }
        }
        for(int k=1;k<n;++k)
            LOG("arg[%d]=%s\n", k, newargv[k]);
    #define DEFINE_OP(op) xmp_oper.op = xmp_##op
        printf("mem-xmpfs inited\n");
        umask(0);
        struct fuse_operations xmp_oper = {0};
        DEFINE_OP(getattr);
        DEFINE_OP(readlink);
        DEFINE_OP(mknod);
        DEFINE_OP(mkdir);
        DEFINE_OP(unlink);
        DEFINE_OP(rmdir);
        DEFINE_OP(symlink);
        DEFINE_OP(rename);
        DEFINE_OP(link);
        DEFINE_OP(chmod);
        DEFINE_OP(chown);
        DEFINE_OP(truncate);
        DEFINE_OP(open);
        DEFINE_OP(read);
        DEFINE_OP(write);
        DEFINE_OP(statfs);
        DEFINE_OP(release);
        DEFINE_OP(fsync);
        DEFINE_OP(setxattr);
        DEFINE_OP(getxattr);
        DEFINE_OP(listxattr);
        DEFINE_OP(removexattr);
        DEFINE_OP(opendir);
        DEFINE_OP(readdir);
        DEFINE_OP(releasedir);
        DEFINE_OP(create);
        if(pbench){
            pbench->do_bench();
            return 0;
        } else {
            int ret = fuse_main(n, newargv, &xmp_oper, NULL);
            return ret;
        }
    }
    
    

    4. 编译

    使用cygwin编译的Makefile如下。

    #Makefile by fwt
    CPP       = g++ -DWIDE_OFF_T -D__USE_FILE_OFFSET64 \
     -I. -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE\
    -D_REENTRANT -D_THREAD_SAFE -D__STDC_FORMAT_MACROS \
    -fno-strict-aliasing -fsigned-char -Wno-invalid-offsetof \
    -fno-builtin-malloc -fno-builtin-calloc \
    -fno-builtin-realloc -fno-builtin-free -DPIC -std=c++17 -O1  
    
    %.o: %.cpp
        $(CPP) -w -c $^ -o $@
    
    XMP_MEM_OBJECTS=dokanfuse.o \
        fuse_helpers.o \
        fuse_opt.o \
        fusemain.o \
        fusexmp-mem.o \
        utils.o
    
    ALL: xmp-mem.exe
    
    xmp-mem.exe: $(XMP_MEM_OBJECTS) dokan1.lib  
        $(CPP) $(CFLAGS) $(CLIBS) -o $@ $^  -unicode
        @echo "**************************************************************"
        @echo "MAKE "$@" FINISH"
        @echo "**************************************************************"
    
    clean:
        rm -f  $(XMP_MEM_OBJECTS)   xmp-mem.exe
    

    执行make -j4即可编译。

    5.运行测试

    5.1 运行挂载

    采用默认参数挂载,挂载盘符M。

    $ ./xmp-mem.exe M:
    mem-xmpfs inited
    

    默认使用8GB内存,增加-m可以指定内存大小,例如使用16GB内存,则执行命令./xmp-mem.exe -m 16 M:即可。
    打开资源管理器,检验挂载成果。

    挂载出来的M盘
    如上图,我们成功的挂载出了M盘,注意查看M盘的使用空间,正是8GB。

    5.2 基本功能测试

    进入M盘,我们可以创建文件。


    创建文本文件

    打开这个文本写一点数据进去。


    写入文本

    还可以复制一个大文件进去。


    从本地硬盘复制文件到M盘

    复制完成后如下。


    完成复制大文件

    当前已经有两个文件,总量约4.2GB,再检查空间的可用量,已经减少到3G多。


    使用量统计

    删除文件,可用量又恢复了。


    删除文件,可用量恢复

    5.3 卸载

    最后是卸载M盘,打开任务管理器,找到进程然后杀死。


    任务管理器

    相关文章

      网友评论

          本文标题:基于dokan的内存文件系统实现

          本文链接:https://www.haomeiwen.com/subject/ksanwctx.html