淘宝分布式文件系统引擎(五) 内存映射实战

完整的源码在下面这个链接哟
github源码

在这感谢我们的小马哥,天天带我们玩些企业开发中的一些我没见过的操作,接下来是关于内存映射这一模块的实战了(这里的的代码是在Linux环镜中运行,都是可以直接运行的哟),搞起!!!!!

关于头文件的一些注意事项

首先我们要知道为了避免重复包含头文件而引发的一些不必要的错误我们要利用#ifndef和#endif来实现避免重复包含
#ifndef QINIU_LARGEFILE_MMAPFILE_H_
#define QINIU_LARGEFILE_MMAPFILE_H_
......//中间的代码块
#endif
而对于我们常用的一些常规头文件我们可以放在一个公共头文件"common.h"中
#ifndef _COMMON_H_INCLUDE_
#define _COMMON_H_INCLUDE_


#include<string>
#include<iostream>
#include<sys/types.h> 
#include<sys/stat.h> 
#include<fcntl.h>
#include<string.h>
#include<stdint.h>
#include<unistd.h>
#include<errno.h>
#include<stdio.h>
#include<sys/mman.h> 
#include<stdlib.h>
#include<inttypes.h>

#endif /*_COMMON_H_INCLUDE_*/
如果我们在开发过程中要注意层次结构,让我们的代码看起来层次分明,对于这样的目的我这里采用的是利用命名空间来使我们的整个层次非常分明
namespace qiniu
{
	namespace largefile
	{
		.....//在这定义各种类
	}
}

内存映射操作头文件

在这开始我就不写什么了,避免整个博客篇幅太长,很多注释都在代码中了
#ifndef QINIU_LARGEFILE_MMAPFILE_H_
#define QINIU_LARGEFILE_MMAPFILE_H_
#include"common.h"
namespace qiniu
{
	namespace largefile
	{
		//调整文件映射的一些选项(根据实际情况自己可以去设置)
		struct MMapOption
		{
			int32_t max_mmap_size_;   //最大映射大小,3M
			int32_t first_mmap_size_; //第一次设置的映射大小,4K
			int32_t per_mmap_size_;   //每次追加多少,4K
		};
		class MMapFile
		{
			public:
			 MMapFile();
			 //explici关键字避免该构造函数隐式转换
			 explicit MMapFile(const int fd);
			 MMapFile(const MMapOption& mmap_option,const int fd);
				
			 ~MMapFile();
			 bool sync_file(); //同步内存数据到文件
			 bool map_file(const bool write=false);  //真正的映射同时设置一些权限
			 
			 void* get_data()const;    //获取映射到内存数据的首地址
			 int32_t get_size()const;  //获取映射数据大小
			 
			 bool munmap_file();  //解除映射
			 bool remap_file();  //重新映射
			  
			private:
			 bool ensure_file_size(const int32_t size);  //调整文件大小
			
			private:
			 int32_t size_;  //现在已经映射内存的大小
			 int fd_;
			 void* data_;	 //映射数据的首地址
			 
			 struct MMapOption mmap_file_option_;
		};
	}
	
}

#endif  /*QINIU_LARGEFILE_MMAPFILE_H_*/

内存映射之cpp源码实现

#include"mmap_file.h"
#include<stdio.h>
static int debug=1;  //调试开关:0为不打开,1为打开


namespace qiniu
{
	namespace largefile
	{
		//初始化列表
		MMapFile::MMapFile():size_(0),fd_(-1),data_(NULL){
		}
		MMapFile::MMapFile(const int fd):size_(0),fd_(fd),data_(NULL){
		}
		
		MMapFile::MMapFile(const MMapOption& mmap_option,const int fd):size_(0),fd_(fd),data_(NULL){
			mmap_file_option_.max_mmap_size_=mmap_option.max_mmap_size_;
			mmap_file_option_.first_mmap_size_=mmap_option.first_mmap_size_;
			mmap_file_option_.per_mmap_size_=mmap_option.per_mmap_size_;
		}
		
		MMapFile::~MMapFile(){
			if(data_){
				if(debug)printf("mmap_file destruct fd:%d, data:%p, mmap size:%d\n",fd_,data_,size_);
				msync(data_,size_,MS_SYNC);  //同步操作
				munmap(data_,size_);
				
				size_=0;
				data_=NULL;
				fd_=-1;
				
				mmap_file_option_.max_mmap_size_= 0;
				mmap_file_option_.first_mmap_size_= 0;
				mmap_file_option_.per_mmap_size_= 0;
			}
		}
		
		//同步文件
		bool MMapFile::sync_file(){
			if(data_!=NULL&&size_>0){
				if(msync(data_,size_,MS_ASYNC)==0)return true;
				return false;
			}
			return true;
		}
		
		//具体文件映射
		bool MMapFile::map_file(const bool write){
			int flags=PROT_READ;
			if(write){
				flags|=PROT_WRITE;
			}
			
			if(fd_<0){
				return false;
			}
			
			//如果最大映射内存大小为0 return false;
			if(0==mmap_file_option_.max_mmap_size_){
				return false;
			}
			
			if(size_<mmap_file_option_.max_mmap_size_){
				//分配第一次可以分配的大小(这个是根据自己的需要进行操作,并不一定要这样)
				size_=mmap_file_option_.first_mmap_size_;
			}else{
				size_=mmap_file_option_.max_mmap_size_;
			}
			
			if(!ensure_file_size(size_)){
				fprintf(stderr,"ensure file size faild in map_file,size:%d\n",size_);
			}
			
			//void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
			data_=mmap(0,size_,flags,MAP_SHARED,fd_,0);
			
			if(data_==MAP_FAILED){
				//定向到控制台,获取出错原因
				fprintf(stderr,"map file faild:%s",strerror(errno));
				size_=0;
				fd_=-1;
				data_=NULL;
				return false;
			}
			if(debug){
				printf("mmap file successed ,fd: %d ,mapped size: %d ,data: %p \n",fd_,size_,data_);
			}
			return true;
		}
		
		//获取映射到内存数据的首地址
		void* MMapFile::get_data()const{  
			return data_;
		}
		
		//获取映射数据大小
		int32_t MMapFile::get_size()const{  
			return size_;
		}
		
		//解除映射
		bool MMapFile::munmap_file(){
			if(munmap(data_,size_)==0){
				return true;
			}else{
				return false;
			}
		}
		
		//调整文件大小
		bool MMapFile::ensure_file_size(const int32_t size){
			//存放文件的状态信息
			struct stat s;
			//fstat(int filedes,struct stat *s);
			if(fstat(fd_,&s)<0){
				fprintf(stderr,"fstat error,error desc:%s\n",strerror(errno));
				return false;
			}
			
			//如果文件的大小比我们内存中映射的小,进行调整
			if(s.st_size<size){
				if(ftruncate(fd_,size)<0){
					fprintf(stderr,"ftruncate error,size:%d ,error desc:%s\n",size_,strerror(errno));
					return false;
				}
			}
			//fprintf(stderr,"ftruncate success,size:%d\n",s.st_size);
			return true;
		}
		
		//追加映射
		bool MMapFile::remap_file(){
			if(fd_<0||data_==NULL){
				fprintf(stderr,"mremap notmapped yet\n");
				return false;
			}
			if(size_==mmap_file_option_.max_mmap_size_){
				fprintf(stderr,"already mapped max size,now size:%d max size:%d",size_,mmap_file_option_.max_mmap_size_);
				return false;
			}
			int32_t new_size=size_+mmap_file_option_.per_mmap_size_;
			if(new_size>mmap_file_option_.max_mmap_size_){
				new_size=mmap_file_option_.max_mmap_size_;
			}
			
			//同步文件大小
			if(!ensure_file_size(new_size)){
				fprintf(stderr,"ensure file size faild in map_file,size:%d\n",new_size);
				return false;
			}
			if(debug)printf("mremap start fd:%d,new size:%d, old data:%p\n",fd_,new_size,data_);
			
			void*new_map_data=mremap(data_,size_,new_size,MREMAP_MAYMOVE);
			if(MAP_FAILED==new_map_data){//如果映射失败
				fprintf(stderr,"mremap failed,fd:%d  new size:%d,error desc:%s\n",fd_,new_size,strerror(errno));
				return false;
			}else{
				if(debug)printf("mremap success fd:%d,new size:%d, old data:%p\n",fd_,new_size,new_map_data);
			}
			
			size_=new_size;
			data_=new_map_data;
			return true;
		}
		
	}
}

主方法

#include"mmap_file.h"
#include"common.h"
using namespace std;
using namespace qiniu;
static const mode_t OPEN_MODE=0644;//用户本人6:110可读可写,同组其他成员的权限4:100可读,其他成员4:100可读,其他成员4
const static largefile::MMapOption mmap_option={10240000,4096,4096};

int open_file(string file_name,int open_flags){
	int fd=open(file_name.c_str(),open_flags,OPEN_MODE);//打开文件
	if(fd<0){
		return -errno;//error strerror(error)
	}
}


int main(){
	const char*filename="./mapfile_test.txt";
	//1.打开或者创建文件,取句柄,open函数
	int fd=open(filename,O_RDWR|O_CREAT|O_LARGEFILE);
	//int fd=open(filename,O_RDWR|O_LARGEFILE);
	if(fd<0){
		fprintf(stderr,"open file failed filename:%s  error reason:%s\n",filename,strerror(-fd));//相对于是strerror(errno)
		return -1;
	}
	
	largefile::MMapFile*map_file=new largefile::MMapFile(mmap_option,fd);
	//映射搞起
	bool is_mapped=map_file->map_file(true);
	//防御性编程
	if(is_mapped){
		map_file->remap_file();
		printf("map_file size:%d  and  data:%p\n",map_file->get_size(),map_file->get_data());
		memset(map_file->get_data(),'9',map_file->get_size());//写入内存中
		
		//同步文件
		map_file->sync_file();
		
		//解除映射
		map_file->munmap_file();
	}else{
		fprintf(stderr,"map file faild\n");
	}
	
	close(fd);
	
	return 0;
}

运行结果

编译成功,并运行
编译成功
成功创建了mapfile_test.txt文件
在这里插入图片描述

mapfile_test.txt文件成功进行读写了
在这里插入图片描述

这个部分结束了,这个是可以直接在linux下直接运行的,如果有兴趣可以试试,希望这个对你有帮助,谢谢(*^_^*)
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值