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盘的使用空间,正是8GB。
5.2 基本功能测试
进入M盘,我们可以创建文件。
打开这个文本写一点数据进去。
还可以复制一个大文件进去。
复制完成后如下。
当前已经有两个文件,总量约4.2GB,再检查空间的可用量,已经减少到3G多。
删除文件,可用量又恢复了。
5.3 卸载
最后是卸载M盘,打开任务管理器,找到进程然后杀死。