Linux系统编程之内存映射

概述

内存映射是操作系统提供的一种机制,使得文件或设备的内容可以直接映射到进程的虚拟地址空间中。这意味着,我们可以像访问数组一样读写文件内容,而不需要显式地调用I/O函数进行数据传输。内存映射适用于多种应用场景,包括但不限于:大文件处理、数据库和缓存、进程间通信、内存映射文件等。

内存映射的优势主要体现在如下三个方面。

1、提高性能:减少了从磁盘到用户空间的数据复制过程。

2、简化编程模型:可以通过指针直接访问文件内容,就像操作普通内存一样。

3、支持共享内存:多个进程可以共享同一块内存映射区域,实现高效的进程间通信。

mmap和munmap

mmap函数用于创建一个新的内存映射或扩展现有的映射,以将文件或设备映射到内存中。通过mmap,我们可以直接访问文件内容,就像操作内存一样简单和高效。这种机制特别适用于需要处理大文件、实现进程间通信、或需要频繁读写特定文件部分的应用场景。其函数原型如下。

cpp 复制代码
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

各个参数和返回值的含义如下。

addr:建议的映射起始地址,通常设置为NULL,让操作系统自动选择合适的地址。

length:映射区域的长度,以字节为单位。

prot:内存保护标志,指定映射区域的访问权限,取值如下。

(1)PROT_READ:映射区域可以被读取。

(2)PROT_WRITE:映射区域可以被写入。

(3)PROT_EXEC:映射区域可以被执行。

(4)PROT_NONE:映射区域不可访问。

flags:指定映射类型的标志,取值如下。

(1)MAP_SHARED:变更会被共享给其他进程,并且会同步更新到文件中。

(2)MAP_PRIVATE:创建一个私有副本,变更仅对当前进程可见,不会同步回文件。

(3)MAP_ANONYMOUS:映射匿名内存,不关联任何文件描述符。

fd:文件描述符。如果使用了MAP_ANONYMOUS标志,则此参数应设为-1。

offset:文件中的偏移量,指定了映射区域在文件中的起始位置。

返回值:成功时返回指向映射区域的指针,失败时返回MAP_FAILED,并设置errno为具体的错误原因。

munmap函数用于解除之前通过mmap建立的内存映射。它会将指定的地址范围从调用进程的地址空间中删除,并释放相应的资源。其函数原型如下。

cpp 复制代码
int munmap(void *addr, size_t length);

各个参数和返回值的含义如下。

addr:要解除映射的内存区域的起始地址。

length:内存区域的长度。

返回值:成功时返回0,失败时返回-1,并设置errno为具体的错误原因。

实战代码

下面的实战代码使用内存映射技术来读取并打印一个文件的内容。

首先,我们通过调用open函数以只读模式打开名为"hope_wisdom.txt"的文件。接着,使用fstat函数获取文件的统计信息,包括文件大小。然后,通过mmap函数将整个文件内容映射到进程的地址空间中,映射区域设置为只读且私有。这就意味着,对映射内容的任何修改都不会反映到原文件上。

成功映射后,我们直接将映射区域的内容作为字符串打印出来。最后,通过munmap函数解除了文件内容的内存映射,并关闭了文件描述符。

cpp 复制代码
#include <stdio.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>

int main()
{
    int fd = open("hope_wisdom.txt", O_RDONLY);
    if (fd == -1)
    {
        printf("open failed\n");
        return 1;
    }

    // 获取文件大小
    struct stat sb;
    if (fstat(fd, &sb) == -1)
    {
        printf("fstat failed\n");
        close(fd);
        return 1;
    }
    
    // 映射文件
    off_t length = sb.st_size;
    void *pAddr = mmap(NULL, length, PROT_READ, MAP_PRIVATE, fd, 0);
    if (pAddr == MAP_FAILED)
    {
        printf("mmap failed\n");
        close(fd);
        return 1;
    }

    // 打印内容
    printf("%s", (char *)pAddr);

    // 解除映射
    munmap(pAddr, length);
    close(fd);
    return 0;
}
相关推荐
skywalk81638 小时前
在FreeBSD 14.3上部署轻量级Linux jail环境 仅仅占用10M内存
linux·运维·服务器·虚拟机·轻量化·freebsd·jail
知南x8 小时前
【STM32MP157 异核通信框架学习篇】(10)Linux下Remoteproc相关API (下)
linux·stm32·学习
Tipriest_8 小时前
Linux 环境变量的添加与查看详解
linux·环境变量
牢七9 小时前
新linux
linux
HIT_Weston12 小时前
27、【Ubuntu】【远程开发】内网穿透:CA 签名
linux·运维·ubuntu
阿巴~阿巴~12 小时前
基于UDP协议的英汉翻译服务系统:从网络通信到字典查询的完整机制
linux·服务器·网络·网络协议·udp协议·套接字绑定·英汉翻译服务系统
阿巴~阿巴~12 小时前
简易回声服务器实现与网络测试指南
linux·服务器·网络·udp协议·网络测试·udp套接字编程
凡间客15 小时前
Ansible安装与入门
linux·运维·ansible
君以思为故15 小时前
认识Linux -- 进程概念
linux·服务器
_OP_CHEN15 小时前
Linux网络编程:(八)GCC/G++ 编译器完全指南:从编译原理到实战优化,手把手教你玩转 C/C++ 编译
linux·运维·c++·编译和链接·gcc/g++·编译优化·静态链接与动态链接