Linux下对mmap封装使用

这篇具有很好参考价值的文章主要介绍了Linux下对mmap封装使用。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1、mmap简介

mmap即memory map,是一种内存映射文件的技术。mmap可以将一个文件或者其它对象映射到进程的地址空间,进而实现磁盘地址和进程虚拟地址的一一对应关系。通过使用mmap技术,我们可以让不同进程通过映射到同一个普通文件的方式实现共享内存,普通文件被映射到进程地址空间当中之后,进程可以向访问普通内存一样对文件高效地进行一系列操作。

2、Linux下mmap使用介绍

2.1、mmap函数

mmap() 必须以 PAGE_SIZE(默认为4KB) 为单位进行映射,而内存也只能以页为单位进行映射,若要映射非 PAGE_SIZE 整数倍的地址范围,要先进行内存对齐,强行以 PAGE_SIZE 的倍数大小进行映射。函数声明如下:

#include <sys/mman.h>
void* mmap(void* start, size_t length, int prot, int flags, int fd, off_t offset);

参数说明:

  • addr: 映射区的开始地址,设置为0时表示由系统决定映射区的起始地址
  • length: 映射区的长度,长度单位是以字节为单位,不足一内存页按一内存页处理
  • port: 映射区域的权限,不能与文件的打开模式冲突,可以通过or运算合理地组合在一起
  • flags: 映射的标志位,可以通过or运算合理地组合在一起
  • fd: 有效的文件描述词,一般是由open()函数返回
  • offset: 文件偏移量

参数 port 的取值如下:

PORT_EXEC:映射区具有可执行权限
PROT_READ:映射区具有可读权限
PROT_WRITE:映射区具有可写权限
PROT_NONE:映射区不可被访问

参数 flags 的取值如下:

MAP_SHARED:对映射区域的写入操作直接反映到文件当中
MAP_FIXED:若在start上无法创建映射则失败(如果没有此标记会自动创建)
MAP_PRIVATE:对映射区域的写入操作只反映到缓冲区当中不会写入到真正的文件
MAP_ANONYMOUS:匿名映射将虚拟地址映射到物理内存而不是文件(忽略fd)
MAP_DENYWRITE:拒绝其它文件的写入操作
MAP_LOCKED:锁定映射区域保证其不被置换

返回值:成功执行时,mmap() 返回被映射区的指针。失败时,mmap() 返回 MAP_FAILED(其值为(void *)-1),并且 errno 被设为以下的某个值

EACCES:访问出错
EAGAIN:文件已被锁定,或者太多的内存已被锁定
EBADF:fd不是有效的文件描述词
EINVAL:一个或者多个参数无效
ENFILE:已达到系统对打开文件的限制
ENODEV:指定文件所在的文件系统不支持内存映射
ENOMEM:内存不足,或者进程已超出最大内存映射数量
EPERM:权能不足,操作不允许
ETXTBSY:已写的方式打开文件,同时指定 MAP_DENYWRITE 标志
SIGSEGV:试着向只读区写入
SIGBUS:试着访问不属于进程的内存区

2.2、munmap函数

munmap() 声明如下:

#include <sys/mman.h>
int munmap(void* start, size_t length);

参数说明:

  • start: 映射区的指针,即mmap()的返回值
  • length: 映射区的长度

返回值:成功执行时,munmap() 返回0。失败时,munmap() 返回-1。

3、对mmap进行封装

下面给出 MEM_MAP 类的设计:

#ifndef __MEM_MAP_H
#define __MEM_MAP_H

#include <iostream>
#include <string>
#include <sys/mman.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>

class Noncopyable
{
public:
    Noncopyable() = default;

    ~Noncopyable() = default;

    Noncopyable(const Noncopyable&) = delete;

    Noncopyable& operator=(const Noncopyable&) = delete;
};

#define MAX_PATH_LEN    200

class MEM_MAP : private Noncopyable
{
public:
    MEM_MAP();

    ~MEM_MAP();

    //取消映射
    void unmap();

    //取消映射并删除对应文件
    void remove();

    //映射指定的文件
    int map(const char* filename,
        size_t length,
        int flags = O_RDWR | O_CREAT,
        mode_t mode = 0644,
        int prot = PROT_READ | PROT_WRITE,
        int share = MAP_SHARED,
        void* addr = NULL,
        off_t offset = 0);

public:
    //将数据立即刷到文件里(MS_ASYNC、MS_SYNC、MS_INVALIDATE)
    int sync(size_t len, int flags = MS_SYNC);
    int sync(int flags = MS_SYNC);

public:
    //返回基地址
    void* addr() const;
    
    //返回映射大小
    size_t size() const;

    //返回文件描述符
    int fd() const;

    //返回文件名
    const char* filename() const;

private:
    //mmap基地址
    void* m_addr;
    //mmap大小
    size_t m_length;
    //mmap文件描述符
    int m_fd;
    //mmap文件名
    char m_filename[MAX_PATH_LEN];
};

#endif /*__MEM_MAP_H*/

以下是 MEM_MAP 类各个方法的具体实现:

#include "mem_map.h"


MEM_MAP::MEM_MAP()
  : m_addr(NULL),
    m_length(0),
    m_fd(-1)
{
    memset(m_filename, 0, sizeof(m_filename));
}

MEM_MAP::~MEM_MAP()
{
    unmap();
}

void MEM_MAP::unmap()
{
    if (m_addr != NULL)
    {
        munmap(m_addr, m_length);
    }
    
    if (m_fd != -1)
    {
        close(m_fd);
    }
    
    m_addr = NULL;
    m_length = 0;
    m_fd = -1;
    memset(m_filename, 0, sizeof(m_filename));
}

void MEM_MAP::remove()
{
    char filename[MAX_PATH_LEN];
    strncpy(filename, m_filename, MAX_PATH_LEN);

    unmap();

    if (filename[0] != '\0')
    {
         unlink(filename);
    }
}

int MEM_MAP::map(const char* filename,
                 size_t length,
                 int flags,
                 mode_t mode,
                 int prot,
                 int share,
                 void* addr,
                 off_t offset)
{
    //取消旧的映射
    unmap();

    //数据赋值
    m_length = length;
    strncpy(m_filename, filename, MAX_PATH_LEN);

    //打开文件描述符
    m_fd = open(m_filename, flags, mode);
    if (m_fd == -1)
    {
        std::cout << strerror(errno) << std::endl;
        unmap();
        return -1;
    }

    //指定文件大小
    if (truncate(m_filename, m_length) == -1)
    {
        std::cout << strerror(errno) << std::endl;
        unmap();
        return -2;
    }

    //建立映射
    m_addr = mmap(addr, length, prot, share, m_fd, offset);
    if (m_addr == MAP_FAILED)
    {
        std::cout << strerror(errno) << std::endl;
        m_addr = NULL;
        unmap();
        return -3;
    }

    return 0;
}

int MEM_MAP::sync(size_t len, int flags)
{
    return msync(m_addr, len, flags);
}

int MEM_MAP::sync(int flags)
{
    return msync(m_addr, m_length, flags);
}

void* MEM_MAP::addr() const
{
    return m_addr;
}

size_t MEM_MAP::size() const
{
    return m_length;
}

int MEM_MAP::fd() const
{
    return m_fd;
}

const char* MEM_MAP::filename() const
{
  return m_filename;
}

4、对封装类MEM_MAP进行测试

测试代码如下,运行之后会生成一个拥有315行 Hello World! 的文件,且末尾会剩余一个字节

#include "mem_map.h"


int main(int argc, char* argv[])
{
    MEM_MAP mmap;
    
    if (mmap.map("test.mmap", 4 * 1024) != 0)
    {
        return -1;
    }

    char* str = (char*)mmap.addr();

    while (1)
    {
        if (strlen(str) + strlen("Hello World!\n") > mmap.size())
        {
            break;
        }
        strcat(str, "Hello World!\n");
    }

    return 0;
}

5、mmap原理

mmap实现内存映射的过程主要分为以下三个阶段:

1. 创建映射区域

过程:进程在用户空间调用 mmap() ,此时进程会在当前进程的地址空间当中寻找一段连续的空闲虚拟地址,并给这块虚拟地址分配一个 vm_area_struct 结构(会自动对各个区域进行初始化),然后将新键的虚拟结构插入到虚拟地址空间的链表或者红黑树当中。

2. 实现物理内存地址和虚拟地址的映射关系

过程:通过待映射的文件描述符指针,在文件描述符表当中找到对应的文件描述符链接到内核已经打开的文件描述符集当中的 struct_file,这个 struct_file 维护着这个被打开的文件的各项信息,通过这个文件的结构体链接到 file_operations,调用内核的mmap函数(原型为 int mmap(struct file* filp, struct vm_area_struct* vma),注意这个不是用户态的mmap),内核mmap函数通过虚拟文件系统当中的inode定位到文件的物理地址,通过 reamp_pfn_range() 建立页表即实现了文件地址和虚拟地址的映射关系。

3. 实现虚拟地址上的数据同步到磁盘文件中

过程:进程的读或写操作访问虚拟地址空间这一段映射地址,通过查询页表,发现这一段地址并不在物理页面上,因为目前只建立了地址映射,真正的硬盘数据还没有拷贝到内存中,因此引发缺页异常。缺页异常进行一系列判断,确定无非法操作后,内核发起请求调页过程。调页过程先在交换缓存空间(swap cache)中寻找需要访问的内存页,如果没有则调用 nopage() 把所缺的页从磁盘装入到主存中,之后进程即可对这片主存进行读或者写的操作,如果写操作改变了其内容,一定时间后系统会自动回写脏页面到对应磁盘地址(由 flags 参数决定是否会自动回写),即完成了写入到文件的过程。

6、源代码下载

下载地址:mmap封装测试代码文章来源地址https://www.toymoban.com/news/detail-465781.html

到了这里,关于Linux下对mmap封装使用的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Go mmap 文件内存映射

    mmap是个很好用的内存映射工具,它可以将文件映射到内存中,可以方便地操作文件。使用mmap的优点是: 内存映射可以使得读写文件的性能更高,因为操作的是内存而不是磁盘。 可以方便地操作文件,不需要再通过文件操作的方式打开读写文件。 方便多线程操作文件。 mma

    2024年02月09日
    浏览(37)
  • Linux 内存模型(Memory: the flat, the discontiguous, and the sparse)

    计算机系统中的物理内存是一种宝贵的资源,因此人们付出了大量的努力来有效地管理它。由于现代系统上存储器体系结构的复杂性,这项任务变得更加困难。有几个抽象层处理如何布局物理内存的细节;其中一个简单地称为“内存模型”。内核中支持三个模型,但其中一个

    2024年02月14日
    浏览(38)
  • android studio内存分析之Memory profiler的使用

    Android Studio中内存分析工具Memory profiler的使用 参考文章 1. 打开Memory Profiler 有两种方式打开,第一种通过标题栏打开: 第二种通过下方菜单栏打开 2. 工具使用 打开后是这样的: 打开后,点击 + 号,选择自己包名 选择完成后,就会创建你项目的SESSIONS界面: 如果想删除这个

    2024年02月05日
    浏览(50)
  • 从内核世界透视 mmap 内存映射的本质(原理篇)

    本文基于内核 5.4 版本源码讨论 之前有不少读者给笔者留言,希望笔者写一篇文章介绍下 mmap 内存映射相关的知识体系,之所以迟迟没有动笔,是因为 mmap 这个系统调用看上去简单,实际上并不简单,可以说是非常复杂的一个系统调用。 如果想要给大家把 mmap 背后的技术本质

    2024年02月08日
    浏览(39)
  • 从内核世界透视 mmap 内存映射的本质(源码实现篇)

    本文基于内核 5.4 版本源码讨论 通过上篇文章 《从内核世界透视 mmap 内存映射的本质(原理篇)》的介绍,我们现在已经非常清楚了 mmap 背后的映射原理以及它的使用方法,其核心就是在进程虚拟内存空间中分配一段虚拟内存出来,然后将这段虚拟内存与磁盘文件映射起来,

    2024年02月08日
    浏览(57)
  • Eclipse内存分析器 Java内存分析工具MAT(Memory Analyzer Tool)的介绍与使用

    =============================================------------------------ 然后我将堆内存文件转储,就告诉我内存泄漏,分析结果如下,请帮我前后理解关系。我需要向领导汇报。7 instances of “org.apache.catalina.loader.ParallelWebappClassLoader”, loaded by “java.net.URLClassLoader @ 0x4c0008ae8” occupy 17,693,472 (1

    2024年04月26日
    浏览(52)
  • 【IMX6ULL驱动开发学习】19.mmap内存映射

    mmap将一个文件或者其它对象映射进内存 ,使得应用层可以直接读取到驱动层的数据,无需通过copy_to_user函数 可以用于像LCD这样的外设, 需要读写大量数据的 一、应用层 mmap用法: 用open系统调用打开文件, 并返回描述符fd. 用mmap建立内存映射, 并返回映射首地址指针start. 对映

    2024年02月16日
    浏览(48)
  • 4个python内存性能检测工具:memory_profiler、timeit、line_profiler、heartrate的使用案例

    这里总结了4个比较好的python性能检测工具,包括内存使用、运行时间、执行次数等方面。 1、memory_profiler查看内存的使用情况 memory_profiler可以用来测量python进程的内存使用情况。可以按行查看内存的使用情况。 memory_profiler 是一个监控进程内存消耗的模块,可以逐行分析 Py

    2024年02月01日
    浏览(41)
  • Linux下mmap

    目录 一.mmap简介 二.为什么需要使用mmap 三.mmap的使用 四.mmap原理 什么是mmap了?从名字上来看是memory map也就是地址映射,是一种内存映射文件的方法。mmap是一个可以将一个文件或者其它对象映射到进程的地址空间实现磁盘的地址和进程虚拟地址空间一段虚拟地址的一一对应关

    2024年02月05日
    浏览(29)
  • 了解Linux 的 mmap --- 笔记

     学习这篇博客,进行了一些归纳 Linux下mmap_linux mmap_一个山里的少年的博客-CSDN博客 https://blog.csdn.net/qq_56999918/article/details/127070280 读取文件 读取文件方法:由操作系统提供的两个方法,read和write来读写文件。 由于read和write是系统调用,需要先从用户态进入到内核态,再将磁

    2024年02月14日
    浏览(32)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包