头文件
MMapFileOperation
类继承自 FileOperation
类,通过继承,子类可以复用基类的文件操作功能。
可以更方便地进行文件的内存映射操作,提供了一套简洁的接口,同时充分利用了继承和组合的思想,重写了父类文件操作类的读写操作,专门用于在内存映射时的读写。
mmap_file_op.h
#ifndef AIRWAVE_LARGEFILE_MMAPFILE_OP_H_
#define AIRWAVE_LARGEFILE_MMAPFILE_OP_H_
#include"common.h"
#include"mmap_file.h"
#include"file_op.h"
namespace Airwave
{
namespace largefile
{
class MMapFileOperation : public FileOperation
{
public:
MMapFileOperation(const std::string& filename, const int open_flags = O_RDWR | O_LARGEFILE | O_CREAT)
:FileOperation(filename,open_flags),mmap_file_(NULL),is_mmapped_(false)
{
//空函数体
}
~MMapFileOperation()
{
if (mmap_file_ != NULL)
{
delete mmap_file_;
mmap_file_ = NULL;
}
}
int mmap_file(const MMapOption& mmap_option); //将文件映射到内存中,提供映射的参数
int munmap_file(); //解除映射
int pread_file(char* buf,const int32_t size,const int64_t offset); //从文件中指定偏移位置读取数据
int pwrite_file(const char* buf,const int32_t size,const int64_t offset); //在文件的指定偏移位置写入数据
void* get_map_data() const; //获取映射的数据
int flush_file(); //把文件立即写入磁盘
private:
MMapFile* mmap_file_; //创建文件映射操作对象
bool is_mmapped_; //是否映射标志
};
}
}
#endif // !Airwave_LARGEFILE_MMAPFILE_OP_H_
mmap_file_op.cpp
#include"mmap_file_op.h"
#include"common.h"
static int debug = 1;
namespace Airwave
{
namespace largefile
{
//内存映射
int MMapFileOperation::mmap_file(const MMapOption& mmap_option)
{
if(mmap_option.max_mmap_size_ < mmap_option.first_mmap_size_) //确保最大映射大于首次映射
{
return TFS_ERROR;
}
if(mmap_option.max_mmap_size_ <= 0) //确保最大映射大于0
{
return TFS_ERROR;
}
int fd = check_file(); //检查文件是否打开
if(fd < 0)
{
fprintf(stderr, "MMapFileOperation::mmap_file - check_file() failed!");
return TFS_ERROR;
}
if(!is_mmapped_) //如果还没有映射
{
if(mmap_file_)
{
delete mmap_file_;
mmap_file_ = NULL;
}
mmap_file_ = new MMapFile(mmap_option, fd); //创建文件映射操作对象
is_mmapped_ = mmap_file_->map_file(true); //将文件映射到内存,同时设置访问权限
}
if (is_mmapped_)
{
return TFS_SUCCESS;
}
else
{
return TFS_ERROR;
}
}
//解除映射
int MMapFileOperation::munmap_file()
{
if(is_mmapped_ && mmap_file_!=NULL)
{
delete mmap_file_;
mmap_file_ = NULL;
is_mmapped_ = false;
}
return TFS_SUCCESS;
}
void* MMapFileOperation::get_map_data() const
{
if(is_mmapped_)
{
return mmap_file_->get_data();
}
return NULL;
}
//
int MMapFileOperation::pread_file(char* buf,const int32_t size,const int64_t offset)
{
//情况一,内存已经映射
//如果读取的数据的偏移量大于等于文件在内存中映射的大小
if(is_mmapped_ && (offset + size)>mmap_file_->get_size())
{
if(debug)
{
fprintf(stdout,"MMapFileOperation::pread_file , size : %d , offset : %ld , map file size : %d, need remap\n" , size, offset,mmap_file_->get_size());
}
mmap_file_->remap_file();
}
if(is_mmapped_ && (offset + size) <= mmap_file_->get_size())
{
memcpy(buf, (char*)mmap_file_->get_data() + offset,size);
return TFS_SUCCESS;
}
//情况二,内存还没有映射,或者要读取的数据映射不全
return FileOperation::pread_file(buf,size,offset);
}
int MMapFileOperation::pwrite_file(const char* buf ,const int32_t size,const int64_t offset)
{
//情况一,内存已经映射
if(is_mmapped_ && (offset + size)>mmap_file_->get_size())
{
if(debug)
{
fprintf(stdout,"MMapFileOperation::pread_file , size : %d , offset : %ld , map file size : %d, need remap\n" , size, offset,mmap_file_->get_size());
}
mmap_file_->remap_file();
}
if(is_mmapped_ && (offset + size) <= mmap_file_->get_size())
{
memcpy((char*)mmap_file_->get_data() + offset,buf,size);
return TFS_SUCCESS;
}
//情况二,内存还没有进行映射,或者映射不全
return FileOperation::pwrite_file(buf,size,offset);
}
int MMapFileOperation::flush_file()
{
if(is_mmapped_)
{
if(mmap_file_->sync_file())
{
return TFS_SUCCESS;
}
else
{
return TFS_ERROR;
}
}
return FileOperation::flush_file();
}
}
}
测试 mmap_file_op_test.cpp
#include"common.h"
#include"mmap_file_op.h"
#include<iostream>
using namespace Airwave;
using namespace std;
const static largefile::MMapOption mmap_option = { 10240000, 4096, 4096 };//内存映射参数:最大映射大小、首次映射大小和每次映射增加的大小
int main(void)
{
int ret = 0;
const char* filename = "mmap_file_op.txt";
largefile::MMapFileOperation* mmfo = new largefile::MMapFileOperation(filename);
int fd = mmfo->open_file();
if (fd < 0)
{
fprintf(stderr, "open file %s failed. reason : %s\n",filename, strerror(-fd));
exit(-1);
}
ret = mmfo->mmap_file(mmap_option);
if(ret == largefile::TFS_ERROR)
{
fprintf(stderr,"mmap_file failed. reason : %s\n",strerror(errno));
mmfo->close_file();
exit(-2);
}
char buffer[128 + 1];
memset(buffer,'6',128);
ret = mmfo->pwrite_file(buffer,128,8);
if (ret < 0)
{
if(ret == largefile::EIXT_DISK_OPER_INCOMPLETE)
{
fprintf(stderr, "pwrite_file %s failed. reason : %s\n",filename, strerror(-ret));
}
else
{
fprintf(stderr, "pwrite_file %s failed. reason : %s\n",filename, strerror(-ret));
}
}
else
{
printf("write file %s success.\n",filename);
}
//第一次读取
memset(buffer, 0, 128);
ret = mmfo->pread_file(buffer, 128, 8);
if (ret < 0)
{
if(ret == largefile::EIXT_DISK_OPER_INCOMPLETE)
{
fprintf(stderr, "read file %s failed. reason : %s\n",filename, strerror(-ret));
}
else
{
fprintf(stderr, "read file %s failed. reason : %s\n",filename, strerror(-ret));
}
}
else
{
buffer[129] = '\0';
printf("read file %s success. content : %s\n",filename, buffer);
}
ret = mmfo->flush_file();
if(ret == largefile::TFS_ERROR)
{
fprintf(stderr,"flush file failed . reason : %s\n",strerror(errno));
}
mmfo->munmap_file();
mmfo->close_file();
return 0;
}
修改写入的偏移量,测试是否会自动扩容
可以看到超出映射的空间后,文件自动进行映射扩容