文件内存映射

内存映射

1. 内存映射的基本概念:

  • 内存映射(Memory Mapping): 将一个文件的内容映射到进程的地址空间,使得对文件的读写等操作可以通过对内存的直接访问来完成,而无需通过传统的读写系统调用。

  • 文件映射到内存的区域: 文件的内容在进程的地址空间中被划分为一些区域,这些区域可以被直接读取或写入。通常包括可读、可写、可执行等不同权限的区域。

2. 内存映射的优势:

  • 性能提升: 由于可以直接在内存中访问文件内容,因此避免了传统的读写系统调用的开销,提高了读写的效率。

  • 进程间通信: 多个进程可以将同一个文件映射到各自的地址空间,实现共享内存,从而方便进程间通信。

  • 文件映射到内存的同步: 对内存的修改可以通过同步到磁盘文件来保持一致,也可以通过共享内存的方式实现多个进程之间的数据共享。

3. 内存映射的实现机制:

  • mmap 系统调用: 在 Unix/Linux 系统中,使用 mmap 系统调用进行内存映射。这个调用会创建一个新的映射区域,将一个文件或者一个设备映射到内存中。

  • munmap 系统调用: 用于解除内存映射。

  • msync 系统调用: 用于将内存映射区域的修改同步到文件。

4. 内存映射的应用场景:

  • 文件 I/O: 提高对文件的读写性能,尤其对于大文件的访问。

  • 共享内存: 进程间通信的一种方式,多个进程可以映射同一个文件,实现数据的共享。

  • 实现类似 mallocfree 的内存管理机制: 通过内存映射技术,可以实现一些自定义的内存分配和释放机制。

5. 注意事项:

  • 文件大小: 文件映射的大小应小于等于文件的大小。

  • 同步: 对内存的修改需要通过 msync 等方法同步到文件,确保数据的一致性。

  • 错误处理: 在使用内存映射时,需要仔细处理错误情况,确保操作的安全性。

文件映射 - mmap实现

common.h

#ifndef _COMMON_H_
#define _COMMON_H_

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

#endif  //_COMMON_H_

 mmap_file.h

#ifndef _MMAP_FILE_H_
#define _MMAP_FILE_H_

#include<unistd.h>      //该头文件包含了 POSIX 系统调用的声明
#include"common.h"

/*
把指定文件映射到内存,提供读写接口
把内存的数据同步到磁盘文件中
*/

namespace Airwave
{
    namespace largefile
    {
        //用于存储内存映射选项的相关参数
        struct MMapOption
        {
            int32_t max_mmap_size_;             //最大映射大小
            int32_t first_mmap_size_;           //首次映射大小
            int32_t per_mmap_size_;             //每次映射增加的大小
        };
        
        //用于管理文件的内存映射操作
        class MMapFile
        {
        public:
            MMapFile();

            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

 mmap_file.cpp

#include"mmap_file.h"
#include<stdlib.h>

static int debug = 1;

namespace Airwave
{
	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_option的相关数值赋值给mmap_file_option_
			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_ != NULL)
			{
				if (debug)
				{
					std::cout<<"mmap file destruct, fd:"<<fd_<<", maped size:"<<size_<<std::endl;
				}

				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 (debug)
				{
					std::cout<<"mmap file sync, fd:"<<fd_<<", maped size:"<<size_<<std::endl;
				}

				return msync(data_, size_, MS_ASYNC) == 0;
			}
			return true;
		}

		bool MMapFile::map_file(const bool write)
		{
			int flags = PROT_READ;

			if (write == true)
			{
				flags |= PROT_WRITE;
			}

			if(fd_ < 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 failed in map_file,size : %d\n",size_);
				return false;
			}

			data_ = mmap(0, size_, flags, MAP_SHARED, fd_, 0);

			if (MAP_FAILED == data_)
			{
				//打印错误信息
				fprintf(stderr, "mmap file failed: %s\n",strerror(errno));	//strerror(errno)返回错误信息,errno是全局变量,表示错误码

				size_ = 0;
				data_ = NULL;	
				fd_ = -1;
				return false;
			}

			if(debug) 			
			{
				std::cout<<"mmap file success, fd:"<<fd_<<", maped size:"<<size_<<" data:"<<data_<<std::endl;	//打印信息"
			}

			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::remap_file()
		{
			if (fd_ < 0 || data_ == NULL)
			{
				fprintf(stderr, "remap file failed, fd:%d, data:%p\n", fd_, data_);
				return false;
			}

			if (size_ == mmap_file_option_.max_mmap_size_)
			{
				fprintf(stderr, "already mapped max size, now size:%d, max_mmap_size:%d\n", 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 failed in remap_file, size:%d\n", new_size);
				return false;
			}

			if(debug) 
			{
				std::cout<<"mremap file start, fd:"<<fd_<<", maped size:"<<size_<<", new size:"<<new_size<<" old data:"<<data_<<std::endl;
			}

			void* new_map_data = mremap(data_, size_, new_size, MREMAP_MAYMOVE);

			if(MAP_FAILED == new_map_data)
			{
				fprintf(stderr, "mremap file failed,new size:%d, error desc: %s\n", new_size, strerror(errno));
				return false;
			}
			else
			{
				if(debug)
				{
					std::cout<<"mremap file success, fd:"<<fd_<<", maped size:"<<new_size<<" new data:"<<new_map_data<<std::endl;	//打印信息"
				}
			}

			data_ = new_map_data;
			size_ = new_size;

			return true;
		}

		bool MMapFile::ensure_file_size(const int32_t size)
		{
			struct stat s;
			if (fstat(fd_, &s) < 0)
			{
				fprintf(stderr, "fstat file failed: %s\n", strerror(errno));
				return false;
			}

			if (s.st_size < size)
			{
				if (ftruncate(fd_, size) < 0)
				{
					fprintf(stderr, "ftruncate file failed,size: %d, error desc: %s\n", size, strerror(errno));
					return false;
				}
			}

			return true;
		}
	}

}

测试 main.cpp

#include"common.h"
#include"mmap_file.h"



using namespace Airwave;
using namespace std;

static const mode_t OPEN_MODE = 0644;			//文件权限,可读可写
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; //errno strerror(error)
	}
	return fd;
}


int main(void)
{
	const char* filename = "test.txt";

	//1.创建/打开一个文件,取得文件的句柄 open函数
	int fd = open_file(filename, O_RDWR | O_CREAT | O_LARGEFILE);

	if(fd < 0)
	{
		fprintf(stderr ,"open file %s failed,\nerror:%s\n",filename,strerror(errno));
		return -1;
	}

	largefile::MMapFile* mmap_file = new largefile::MMapFile(mmap_option, fd);


	//2.将文件映射到内存中 mmap函数
	bool is_mapped = mmap_file->map_file();

	if (is_mapped)
	{
		memset(mmap_file->get_data(), '8', mmap_file->get_size());
		mmap_file->sync_file();

		//解除映射
		mmap_file->munmap_file();
	}
	else
	{
		fprintf(stderr, "map file %s failed,\nerror:%s\n", filename, strerror(errno));
	}

	//关闭文件
	close(fd);

	return 0;

}

  1. 结构体 MMapOption

    • 存储了内存映射选项的相关参数,包括最大映射大小 max_mmap_size_、首次映射大小 first_mmap_size 和每次映射增加的大小 per_mmap_size
  2. MMapFile

    • 构造函数:
      • 提供了三个构造函数,包括默认构造函数、接受文件描述符的构造函数和接受 MMapOption 及文件描述符的构造函数。
    • 成员函数:
      • 提供了一系列用于执行文件同步、将文件映射到内存、获取映射到内存数据的首地址、获取映射大小、解除映射和重新执行映射的函数。
    • 私有成员和函数:
      • 包括文件大小、文件描述符、映射的数据指针以及存储映射选项的结构体。
      • 私有函数 ensure_file_size 用于确保文件大小满足要求。

这段代码是一个封装了文件内存映射操作的类,提供了一些基本的文件操作接口,并包含了一些内存映射的选项。

msync

实现磁盘文件内容于共享内存区中的内容一致,即同步操作。

函数原型 int msync ( void * addr, size_t len, int flags) 

 addr:文件映射到进程空间的地址;

len:映射空间的大小;

flags:刷新的参数设置,可以取值MS_ASYNC/ MS_SYNC;

取值为MS_ASYNC(异步)时,调用会立即返回,不等到更新的完成;    

取值为MS_SYNC(同步)时,调用会等到更新完成之后返回;

返回值 成功则返回0;失败则返回-1;    

mremap

扩大(或缩小)现有的内存映射

函数原型 void * mremap(void *old_address, size_t old_size , size_t new_size, int flags);

头文件

#include <unistd.h>

#include <sys/mman.h>    

addr:     上一次已映射到进程空间的地址;

old_size: 旧空间的大小;

new_size: 重新映射指定的新空间大小;

flags:     取值可以是0或者MREMAP_MAYMOVE,0代表不允许内核移动映射区域,              MREMAP_MAYMOVE则表示内核可以根据实际情况移动映射区域以找到一个符合            new_size大小要求的内存区域

返回值 成功则返回0;失败则返回-1;    

测试

 进行扩容后再测试

 扩容4kb,将所有数据改为9

重新编译运行

扩容成功

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值